GoRunner

Errors

One envelope, one spelling, and diagnostics that name the admissible set.

Every failure — including mistyped paths and unsupported methods — answers inside the same envelope:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "trigger config: \"61 * * * *\" is not a valid cron expression"
  }
}

code is a stable SCREAMING_SNAKE identifier to branch on; message is for humans and logs. One spelling, enforced by GoRunner's own build — you will never meet forbidden on one route and FORBIDDEN on another.

Status mapping

StatusCodes you'll meetNotes
400BAD_REQUEST, VALIDATION_ERRORMalformed JSON says malformed; an oversize body says too large — they are not conflated.
401UNAUTHORIZEDOnly ever on credential problems — never on 400s or 403s.
403FORBIDDENNames the missing permission.
404NOT_FOUNDIncludes cross-tenant resources (non-disclosure).
405METHOD_NOT_ALLOWEDWrong method on a real path — still inside the JSON envelope.
409CONFLICTStale expectedRev, a flow busy under another write, replayed-but-unreadable idempotent responses.
422VALIDATION_ERRORSemantically invalid: unknown piece, bad operator, unpublishable definition.
429RATE_LIMITED, CONCURRENCY_LIMITCarries Retry-After and X-RateLimit-*.
503QUEUE_UNAVAILABLE, WORKER_UNAVAILABLE, STORAGE_UNAVAILABLEA dependency is down: dispatch, the worker fleet (a synchronous webhook with no worker is refused rather than silently queued), or the object store. Carries Retry-After where retrying helps.
5xxINTERNALCarries X-Request-ID for correlation.

Diagnostics that help you fix it

Validation failures don't just refuse — they name the location, the observed value, and the legal alternatives, with the nearest miss first:

{
  "ok": false,
  "diagnostics": [{
    "severity": "error",
    "path": "trigger→check_total",
    "message": "condition operator \"greaterThan\" is not one the engine implements",
    "alternatives": ["num_gt", "num_gte", "text_contains", "..."]
  }]
}

This shape is deliberate: feedback naming the admissible set is what lets a retry loop — scripted or model-driven — converge instead of guessing. POST /api/workflows/validate gives you the full diagnostic report without writing anything; the publish gate refuses on the first blocking diagnostic from the same function, so they can never disagree.

On this page