PlatformHTTP API

Errors

Read Spiral error responses, branch on the right field, and decide whether to retry.

Available

Spiral's APIs return errors in one of three JSON shapes, depending on the service. The Catalog and Applications return a stable errorName you can branch on; other services return a kind or a message. In every case the HTTP status is reliable, so start there. This page shows each shape, which routes use it, and which errors are worth retrying.

Which body a route returns

RoutesError body
Catalog (/api/catalog/...) and Applications (/api/apps/...)Error envelope
Tables and Enrichments (/arbor/...), Locations, Connections, Vector Indexes, Tasks, and worker pools and sessions (/projects/{project_id}/...)Kind body
Organizations, Projects, Storage, workload identities, grants and installations, managed workers, and POST /oauth/token on api.spiraldb.devMessage body
OAuth endpoints on auth.spiraldb.comOAuth error body: error and error_description

Error envelope

{
  "errorCode": "CONFLICT",
  "errorName": "Catalog:IdempotencyKeyReused",
  "errorInstanceId": "0199d5a2-7c4e-7b8a-9a51-2f0c6e8b1d42"
}
FieldMeaning
errorCodeThe canonical code. It selects the HTTP status.
errorNameA stable name in the form <Service>:<Error>. Branch on this field.
errorInstanceIdA UUIDv7 for this occurrence. The server logs the same ID. Include it when you report a problem.
parametersOptional. A map of the error's declared parameters. Each value is a string.

Each operation in the HTTP reference lists the error names that it declares, with their parameters. A server can also return names that the operation does not declare, such as the shared authentication errors. Treat an unknown name by its errorCode.

Codes and statuses

errorCodeStatus
INVALID_ARGUMENT400
UNAUTHENTICATED401
PERMISSION_DENIED403
NOT_FOUND404
CONFLICT409
FAILED_PRECONDITION412
REQUEST_ENTITY_TOO_LARGE413
INTERNAL500
UNAVAILABLE503
TIMEOUT504

Shared errors

These errors can occur on every /api route:

errorNameStatusCause
Default:Unauthenticated401The Authorization header is missing, or the token is not valid.
Default:PermissionDenied403The caller does not have a permission. The operation and resource parameters name it.
Contract:InvalidPath400A path parameter does not decode.
Contract:InvalidQuery400A query parameter does not decode.
Contract:InvalidRequest400The JSON body does not match the request type, contains an unknown field, or repeats a key.
Contract:UnexpectedBody400The operation takes no request body.
Contract:UnexpectedQuery400The operation takes no query parameters.
Contract:UnsupportedMediaType415The Content-Type is not the media type that the operation accepts.
Contract:RequestTooLarge413The body exceeds the size limit of the operation.
Contract:Internal500An unexpected server failure. Only errorInstanceId identifies it.

Internal causes stay in the server logs and never appear in the envelope, so include errorInstanceId when you report a problem.

Kind body

These routes return a JSON object with a kind field:

{
  "kind": "permissionDenied",
  "operation": "table:read",
  "resource": "c0a8012e-5b8f-4c1e-9d3a-6f7e8a9b0c1d"
}
kindStatusOther fields
badRequest400message
unauthorized401message, always Unauthorized
permissionDenied403operation, resource
notFound404resource
conflict409message
internal500message, always Internal server error

Tasks and worker pools also return limitReached (402) and notImplemented (501), each with message.

Message body

These routes return a JSON object with one message field:

{ "message": "Resource `proj_123` not found" }
Statusmessage
400The reason that the request is not valid.
401Unauthorized. The server does not return the reason.
402The limit that the request reached.
403Permission `<operation>` denied on `<resource>`
404Resource `<resource>` not found
409The reason for the conflict.
501The feature that is not implemented.
500Internal server error

Malformed requests on service routes

On service routes, the server decodes the path, the query, and the JSON body before the service runs. A decoding failure returns a plain-text body, not JSON. The status is 400 for malformed JSON or an invalid path or query, 415 for a missing JSON Content-Type, and 422 for a JSON body that does not match the request type.

Retry an error

Do not retry 4xx errors without a change to the request. The exceptions are 401 after a token refresh, and a conflict that you resolve first. For a write that returned 500 or no response, retry with the same IDs; see Retries.

Found a problem? Edit this page or report an issue.

On this page