Skip to content
Stackship documentation Svenska

Stackship platformUsers

Errors

The body a failed API call returns, what each HTTP status code means on the platform, and where to look up an error's code.

The error body

A failed call answers with an HTTP status code and, in most cases, this JSON body:

json
{
  "error": "Access denied. You do not have permission to perform this action.",
  "code": "FORBIDDEN",
  "module": "apps",
  "correlationId": "3f2b9c1e-6d4a-4e0b-9a51-2c7d8e0f4b6a",
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736"
}
Field Meaning
error A sentence describing what went wrong. It is written for people, and its wording can change
code A stable, machine-readable code. Branch on code, not on error. See Look up a code
module The documentation key of the module that answered: kernel for the platform's core, or a module such as apps or db-postgres. An error that the core passes on from a module keeps the module's key, also when the core replaces the rest of the body (see Errors from the gateway)
targetModule Only with MODULE_UNAVAILABLE: the module the core could not reach. It holds the resource type from the request's path, such as postgresclusters, or the module's registration name, such as postgres — not a documentation key like module
correlationId Identifies the request. Send an X-Correlation-Id header and the platform uses — and returns — yours; otherwise it is a generated GUID, or the request's trace ID
traceId Identifies the request's trace in the platform's logs. Not every error has one
details Extra data, only for some codes — for example the field errors of VALIDATION_FAILED

A field without a value is left out, or sent as null. An error from an older module can lack code and module. Some codes add fields of their own; the code's entry in the reference lists them. Quote the correlationId when you report a problem.

Errors in the problem format

Some routes answer in the standard problem format instead, with the content type application/problem+json. The file routes of apps and container instances and the snapshot routes do, among others:

json
{
  "type": "…",
  "title": "backups-not-configured",
  "status": 422,
  "detail": "Backups are not enabled on PostgreSQL cluster 'orders', so there is no archive to snapshot into.",
  "code": "backups-not-configured",
  "remediation": "Enable backups in the cluster's Configuration, wait for the first archive, then snapshot.",
  "module": "db-postgres",
  "correlationId": "3f2b9c1e-6d4a-4e0b-9a51-2c7d8e0f4b6a"
}

This body has no error field. The sentence for people is in detail, which some codes leave empty, and title is either a short summary or the code itself. code, module and correlationId mean the same as above.

A client that reads error when it is present, and otherwise detail and then title, finds a sentence to show in every error.

Status codes

Status Typical code Meaning
400 BAD_REQUEST, MODEL_BINDING_FAILED The request is malformed or a value is invalid. See Validation errors
401 UNAUTHORIZED No token, or a token that is invalid or expired. See Authenticate
403 FORBIDDEN, FORBIDDEN_REQUESTABLE, VAULT_GRANT_FORBIDDEN You are signed in but lack the permission at this scope. See Permission errors
404 NOT_FOUND No such route, or no such resource
405 METHOD_NOT_ALLOWED The route exists but not with this method
409 CONFLICT, DUPLICATE The request conflicts with the current state: the name is taken, the resource is busy with another change, or it changed while you were changing it
412 — An If-Match precondition failed. See Concurrent changes
413 too_large The upload is larger than the route accepts. See File errors
422 VALIDATION_FAILED and others The request is well-formed but refused: it fails validation, or a rule forbids it — for example a compute plan policy or a snapshot refusal
429 REQUEST_FAILED, resend_limit Too many requests to a rate-limited route; try again later. With resend_limit, an invitation has been resent as often as it may be
500 INTERNAL_ERROR An unexpected error on the platform
501 NOT_SUPPORTED The operation is not supported
502 PLATFORM_ERROR, GRPC_ERROR A call from the platform to Kubernetes or to another platform service failed. PLATFORM_ERROR also comes with a 4xx status when Kubernetes refused the change — see PLATFORM_ERROR
503 MODULE_UNAVAILABLE, AUTHORIZATION_UNAVAILABLE, compute_plan_policy_unavailable Something the route depends on cannot answer right now. The code decides whether retrying helps, and its documentation says so — see Look up a code. With MODULE_UNAVAILABLE, AUTHORIZATION_UNAVAILABLE, compute_plan_policy_unavailable and GRPC_ERROR, retry shortly; a module's own 503 codes are explained on its pages. A 503 with REQUEST_FAILED names no cause; if retrying does not help, report it with the correlationId

Look up a code

A code is documented by the module that owns it. Codes that mean the same whichever module answers are documented once, under the platform's core (kernel), even though the body's module names the module that answered: a file error from an app carries "module": "apps", and its code is on File errors. A module's own codes are on that module's pages.

So look a code up by the pair of module and code first and, when that module does not document the code, by kernel and the code. The platform's documentation service answers GET /docs-hub/errors/{module}/{code} for one exact pair; it does not try kernel for you.

The codes documented under the core are on three pages:

  • Error codes — the codes every module can answer with, and the core's own
  • File errors — the file routes of apps and container instances
  • Snapshot errors — why a snapshot or a restore of a PostgreSQL cluster, SQL Server instance or container instance is refused

Validation errors

Validation failures come in these forms, depending on the route:

  • 422 with VALIDATION_FAILED, and one entry per field in details:

    json
    {
      "error": "Validation failed.",
      "code": "VALIDATION_FAILED",
      "details": [{ "field": "Name", "message": "…" }]
    }

    Some checks answer VALIDATION_FAILED with the reason in error and no details.

  • 400 with BAD_REQUEST and the field errors in errors: either in the standard problem format, keyed by field, or as a list of messages:

    json
    {
      "title": "One or more validation errors occurred.",
      "status": 400,
      "errors": { "Region": ["Region is required"] },
      "code": "BAD_REQUEST"
    }
  • 400 with BAD_REQUEST and only a message in error.

Permission errors

A 403 usually does not say which permission was missing; the roles that grant an action are listed on each module's permissions page. Ask an owner of the boundary for the role you need.

When the body's code is FORBIDDEN_REQUESTABLE, the permission can be requested as just-in-time access: details names the action, the scope and the role to request. See Request access. With VAULT_GRANT_FORBIDDEN, a request has been filed for you already; see VAULT_GRANT_FORBIDDEN.

Concurrent changes

Apps support optimistic concurrency: reading an app returns an ETag header, and a PATCH that sends it back as If-Match answers 412 if the app has changed since. Without If-Match, a PATCH that loses a race with another change is applied again to the app as it now is, up to five times, so concurrent patches to different fields all land; only when all five attempts lose does it answer 409. Other resources do not use ETag or If-Match; a conflicting change answers 409.

Errors from the gateway

Requests to a module's routes pass the platform's core first, which forwards them to the module. The core answers some errors itself, with "module": "kernel":

  • 401 with UNAUTHORIZED and "error": "missing bearer token" — for a missing token, and also for one that is invalid or expired.
  • 503 with MODULE_UNAVAILABLE when the module is not running or does not respond. The module it could not reach is in targetModule. See MODULE_UNAVAILABLE.

A module's own 4xx answers are passed on unchanged. A 5xx answer from a module is replaced by {"error": "An internal error occurred.", "code": "…", "module": "…", "correlationId": "…"}, so the detail stays in the platform's logs. The module's code and module are kept, whether the code is in upper or lower case — INTERNAL_ERROR, PLATFORM_ERROR and compute_plan_policy_unavailable alike — as long as they are shaped like a code and a module key; anything else is left out. The replacement body is plain JSON, also when the module answered in the problem format, and has no traceId. The code's documentation says what the error means. A platform that runs in development mode passes 5xx answers on unchanged.

In the CLI

stsh prints the error and exits with a code that tells the failures apart:

Exit code HTTP status
3 401
4 403
5 404
8 422
1 any other failure