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:
{
"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:
{
"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:
422withVALIDATION_FAILED, and one entry per field indetails:{ "error": "Validation failed.", "code": "VALIDATION_FAILED", "details": [{ "field": "Name", "message": "…" }] }Some checks answer
VALIDATION_FAILEDwith the reason inerrorand nodetails.400withBAD_REQUESTand the field errors inerrors: either in the standard problem format, keyed by field, or as a list of messages:{ "title": "One or more validation errors occurred.", "status": 400, "errors": { "Region": ["Region is required"] }, "code": "BAD_REQUEST" }400withBAD_REQUESTand only a message inerror.
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":
401withUNAUTHORIZEDand"error": "missing bearer token"— for a missing token, and also for one that is invalid or expired.503withMODULE_UNAVAILABLEwhen the module is not running or does not respond. The module it could not reach is intargetModule. SeeMODULE_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 |