GoRunner docs
Search or askSearch⌘K

Errors

Every API error has the same shape. Branch on its code, never on its message.

The shape

A success is {"data": …}. A failure is {"error": …}, even for a mistyped path (404 NOT_FOUND) or a wrong method (405 METHOD_NOT_ALLOWED).

json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "…written for a person; may change",
    "details": { "field": "name" }
  }
}

Every answer carries an X-Request-ID header. Quote it when you report a problem. On a self-hosted installation, it is the request_id in the logs.

code
A stable name in capitals, such as CONCURRENCY_LIMIT. Branch on it.
message
One sentence for a person. It uses the installation’s own words and may change.
details
An object, on some errors: what a program can act on, such as the wrong field, the missing permission, the switch that is off or why a token ended (reason). Its keys depend on the code.

Branch on the code

Decide by status first, then by code. Retry only what says to retry, and wait as long as Retry-After asks when it is sent.

const res = await fetch(url, { method: "POST", headers, body });
const answer = await res.json();

if (!res.ok) {
  const { code, message } = answer.error;
  const retryAfter = res.headers.get("Retry-After");
  if (retryAfter) {
    // a 429 or 503 that says when: try again after that many seconds
  } else if (res.status === 429) {
    // CONCURRENCY_LIMIT or RUNS_WAITING_LIMIT: try again when a run finishes
  } else if (code === "PAUSED") {
    // a self-hosted installation without a license: don't retry
  } else if (code === "CONFLICT") {
    // publish the workflow first, or wait for the other request
  } else {
    throw new Error(`${code}: ${message}`); // fix the request
  }
}

What to do, by status

StatusWhat happenedWhat to do
400, 422The request or the workflow is not valid. For a workflow, Check a definition lists every problem and what is allowed.Fix the request
401The token is missing, expired or revoked.Make a new token
403The token may not do this, or the account is suspended.Check its permissions
404Nothing with that ID in the token’s workspace.Check the ID
405The path exists, but not with this method. Allow lists the ones it takes.Use another method
409The thing is in the wrong state, such as not published. Or someone saved the workflow since you read it (expectedRev).Change its state, or read it again
413The body is over 5 MiB (5,242,880 bytes). Malformed JSON is 400, not 413.Send less
415INVALID_CONTENT_TYPE: a Content-Type other than application/json.Send JSON
429A limit was reached.Retry after Retry-After. CONCURRENCY_LIMIT and RUNS_WAITING_LIMIT send none: retry when a run finishes.
500INTERNAL_ERROR: something failed on GoRunner’s side.Retry. If it repeats, send us its X-Request-ID
503GoRunner couldn’t do it just now, the server is holding as much request data as it can (BUSY), or the installation is paused. Nothing was done.Retry after Retry-After. PAUSED sends none: load a license.

A 503 SERVICE_UNAVAILABLE means the database or Redis didn’t answer, so your token and the address weren’t checked. Only a token or an address that really isn’t there answers 401 or 404.

Error codes lists all 75 codes, with the calls that send each one.

When an installation is paused

A self-hosted installation runs only while a valid, unexpired license is loaded. Without one, a call with an API token made in GoRunner answers 503 PAUSED, and nothing is read, written or run. A token made inside a partner’s product answers 403 EMBED_NOT_AVAILABLE instead.

Calls work again within a second of loading a license, or within a minute if Redis is down. GoRunner Cloud never pauses.