Skip to content

Error Code Catalog (Canonical)

Purpose

Define the canonical set of machine-readable code values returned in ErrorResponse.code across all API endpoints. Every error response must include one of these codes so that clients, SDKs, and monitoring systems can react programmatically without parsing message strings.

ErrorResponse Envelope

{
  "code": "allocation_not_found",
  "message": "The requested allocation does not exist or is not accessible.",
  "correlation_id": "01HXYZ...",
  "details": {}
}
- code — machine-readable, one of the values below (stable across versions) - message — human-readable, English, may change without notice - correlation_id — always present; use for support and trace lookup - details — optional structured context (e.g. field validation errors)


Code Catalog

Authentication — 401

Code When returned
token_missing No Authorization header present on a protected endpoint
token_invalid JWT signature or format is invalid
token_expired JWT has passed its exp claim
token_scope_invalid Token lacks the required scope (e.g. terminal token presented outside its allocation)
auth_personal_disabled Personal auth flow is disabled by feature flag in the current environment
auth_enterprise_required Request used personal auth path but input must use enterprise SSO flow
auth_personal_required Request used enterprise auth path but input must use personal auth flow

Authorization — 403

Code When returned
insufficient_permissions Authenticated user lacks the required role or policy permission
admin_required Endpoint requires the admin role
ownership_required Resource belongs to a different user and caller is not an admin

Validation — 400

Code When returned
validation_error One or more request fields failed schema or constraint validation; details contains per-field errors
invalid_request Request body is malformed or unparseable (e.g. not valid JSON)

Allocation — 404 / 409 / 422

Code HTTP When returned
allocation_not_found 404 Allocation UUID does not exist or is not visible to the caller
allocation_not_active 409 Requested action (terminal token, SSH key download) requires active allocation state
allocation_already_releasing 409 Release attempted on an allocation already in releasing or released state
allocation_concurrency_limit 409 User has reached the configured maximum concurrent allocation count
insufficient_balance 409 User balance is too low to cover the requested provision
sku_unavailable 409 No available nodes match the requested SKU

Node — 404 / 409

Code HTTP When returned
node_not_found 404 Node UUID does not exist
node_offline 409 Node was unreachable at provisioning or probe time
node_in_use 409 Node is currently assigned to an allocation; deletion or reassignment blocked
node_already_exists 409 A node with the same host already exists

User — 404 / 409

Code HTTP When returned
user_not_found 404 User UUID does not exist
user_already_exists 409 A user with the same email or username already exists

Billing and Payments — 400 / 409

Code HTTP When returned
stripe_signature_invalid 400 Webhook raw-body signature verification failed
refund_window_exceeded 409 Refund request is outside the configured refund eligibility window

Storage — 404 / 409

Code HTTP When returned
storage_object_not_found 404 Storage path does not exist
storage_path_traversal 400 Path contains traversal sequences (..) that escape the user root
storage_already_exists 409 Target path already exists (mkdir, rename destination)
storage_quota_exceeded 409 Upload would exceed the user's storage quota

Catalog — 404

Code HTTP When returned
sku_not_found 404 SKU identifier does not exist

Apps — 404 / 409

Code HTTP When returned
app_not_found 404 App catalog slug does not exist or is disabled for caller visibility
app_version_not_found 404 Requested app version does not exist for the app
app_not_entitled 409 Project is not entitled to create/use the requested app
app_instance_not_found 404 App instance UUID does not exist or is not visible to caller
shared_runtime_not_found 404 Tenant-owned shared runtime UUID does not exist or is not visible to caller
shared_runtime_attachment_not_found 404 Shared runtime attachment UUID does not exist or is not visible to caller
shared_runtime_worker_not_found 404 Shared runtime worker UUID does not exist or is not visible to caller
shared_runtime_worker_operation_not_found 404 Shared runtime worker operation UUID does not exist or is not visible to caller
app_instance_state_invalid 409 Requested operation is not valid for current app instance lifecycle state
app_instance_quota_exceeded 409 Project/tenant app instance quota or policy limit exceeded
app_artifact_not_found 404 App artifact UUID does not exist or is not visible to caller
app_artifact_already_exists 409 An artifact with the same project and digest is already registered
app_artifact_state_invalid 409 Requested operation is not valid for the current app artifact lifecycle state
os_image_not_found 404 OS image catalog entry UUID does not exist
os_image_already_exists 409 OS image catalog entry with the same slug already exists

Rate Limiting — 429

Code HTTP When returned
rate_limit_exceeded 429 Caller has exceeded the configured rate limit; see Retry-After header

Server — 500 / 502 / 503

Code HTTP When returned
internal_error 500 Unexpected server-side failure; always include correlation_id in support requests
upstream_error 502 A downstream dependency (Stripe, GPU node SSH, Temporal) returned an unexpected failure
service_unavailable 503 Service temporarily unavailable; safe to retry after back-off

Rules

  1. Stable codes: once shipped, a code value must not be renamed or removed — it is a breaking change.
  2. Additive only: new codes may be added in any version without a major bump.
  3. details for validation: validation_error responses must populate details with at minimum { "fields": [{ "field": "...", "issue": "..." }] }.
  4. No free-form codes: implementors must not invent codes outside this catalog. Submit a PR to extend it.
  5. HTTP status is the primary signal: code refines within a status — never use code as a substitute for the correct HTTP status.