Errors
Every stable error code the API, the SDK and the CLI surface, with its HTTP status.
Errors answer { "error": { "code", "message", "retryAfterMs"?, "details"? } }. Codes are stable; messages are not.
| Code | HTTP status | Meaning |
|---|---|---|
UNAUTHENTICATED | 401 | The request carries no credential, or IAM does not accept the one it carries. |
ACCESS_DENIED | 403 | IAM decided the call's action @ resource and denied it. |
CONFIRMATION_REQUIRED | 403 | The caller is an agent acting for a person, and that person must confirm the request first. |
NOT_FOUND | 404 | The route, or the object the input names, does not exist or is not visible to the caller. |
INVALID_INPUT | 400 | The body failed validation: a missing or unknown field, a wrong type, or a value outside its constraints. |
INVALID_STATE | 409 | The object exists but is in a state that does not allow the call, such as uncordoning a node being deleted or extending a released reservation. |
CONFLICT | 409 | The call raced a concurrent change, or reuses an idempotencyKey for a different request, or names something that already exists. |
CAPACITY_UNAVAILABLE | 409 | No placement satisfies the request now: there are not enough free GPUs, or none that match its constraints, in the pools it may use. |
QUOTA_EXCEEDED | 409 | The request would take the tenant or project past a quota, or past a per-tenant limit such as the number of webhooks. |
SPEND_LIMIT_REACHED | 402 | An IAM budget covering this request is spent, and the pool refuses new work when a budget is exceeded. |
UNSUPPORTED | 422 | The request is valid, but the driver, provider or deployment it lands on cannot do it: a provider with no power control, an instance that cannot be resized in place, or a deployment with no SSH CA. |
UPGRADE_REQUIRED | 426 | The peer speaks a protocol version older than the server's minimum. |
RATE_LIMITED | 429 | Too many requests of this kind, from the caller or to a provider behind the call. |
DRIVER_ERROR | 502 | A driver, a node command or a site agent failed, or answered with something compute cannot trust: a BMC operation that failed, an unattested result, or firmware and BIOS settings that did not take. |
PROVIDER_ERROR | 502 | A VPS or bare-metal provider's API refused or failed the call. |
INVALID_CONFIG | 500 | The deployment is configured wrongly, for example an option out of range or a module that needs another one. |
NOT_IMPLEMENTED | 501 | The method or feature is planned for a later phase and not built in this release. |
CSRF_REJECTED | 403 | A POST lacks content-type: application/json or the X-Better-Compute: 1 header, or a cookie-authenticated request has no Origin. |
UNTRUSTED_ORIGIN | 403 | A cookie-authenticated request, or a tunnel's WebSocket upgrade, came from an Origin other than the deployment's own. |
METHOD_NOT_ALLOWED | 405 | The HTTP method is not accepted on this path. |
PAYLOAD_TOO_LARGE | 413 | The request body is larger than the server accepts. |
INTERNAL_ERROR | 500 | An unexpected failure on the server. |
MISSING_DEPENDENCY | 500 | An optional package or service the deployment needs for this call is not installed or not configured, such as PGlite for an embedded database or a remote IAM endpoint. |
CONFIG_DRIFT | 409 | The fleet changed after the plan was made, so fleet.apply refused a plan whose planHash no longer matches. |
UNAVAILABLE | 503 | A dependency the request needs is unreachable for now: the secret store's KMS, a node's gateway, or remote IAM. |
TENANT_SUSPENDED | 403 | The IAM tenant is suspended, so its mutations are refused, or deleted, so every call is refused. |
UNAUTHENTICATED
HTTP 401.
The request carries no credential, or IAM does not accept the one it carries.
Send a valid IAM session or API key. An expired key or a revoked session answers the same way; the message does not say which.
ACCESS_DENIED
HTTP 403.
IAM decided the call's action @ resource and denied it.
Callers outside the input tenant (and outside the root tenant) are refused before any database access. Grant the action in IAM, or call as an identity that holds it. See Actions and resources.
CONFIRMATION_REQUIRED
HTTP 403.
The caller is an agent acting for a person, and that person must confirm the request first.
details carries confirmationId, the action and resource, and expiresAt when IAM set one. Nothing was changed. Once the person confirms, retry with the same idempotencyKey. See Delegated confirmation.
NOT_FOUND
HTTP 404.
The route, or the object the input names, does not exist or is not visible to the caller.
Lookups are scoped to the input tenantId, so an id that belongs to another tenant is not found.
INVALID_INPUT
HTTP 400.
The body failed validation: a missing or unknown field, a wrong type, or a value outside its constraints.
Unknown fields are refused, never ignored. The Input table of each method in the API reference lists every accepted field.
INVALID_STATE
HTTP 409.
The object exists but is in a state that does not allow the call, such as uncordoning a node being deleted or extending a released reservation.
Read the object again and act on its current state. Retrying unchanged fails the same way.
CONFLICT
HTTP 409.
The call raced a concurrent change, or reuses an idempotencyKey for a different request, or names something that already exists.
A concurrent change is safe to retry after reading the object again. A reused key with a different body is not: send a new key for a new request.
CAPACITY_UNAVAILABLE
HTTP 409.
No placement satisfies the request now: there are not enough free GPUs, or none that match its constraints, in the pools it may use.
Retry later, relax the constraints, or book a later window. See Placement.
QUOTA_EXCEEDED
HTTP 409.
The request would take the tenant or project past a quota, or past a per-tenant limit such as the number of webhooks.
Release something, or raise the quota with quotas.set. See Quotas and usage.
SPEND_LIMIT_REACHED
HTTP 402.
An IAM budget covering this request is spent, and the pool refuses new work when a budget is exceeded.
details names the budget (budgetId, name) when IAM reports it. See Billing and usage.
UNSUPPORTED
HTTP 422.
The request is valid, but the driver, provider or deployment it lands on cannot do it: a provider with no power control, an instance that cannot be resized in place, or a deployment with no SSH CA.
UPGRADE_REQUIRED
HTTP 426.
The peer speaks a protocol version older than the server's minimum.
A daemon below proto_min is welcomed but receives only daemon.upgrade until it runs a supported version. See Protocol.
RATE_LIMITED
HTTP 429.
Too many requests of this kind, from the caller or to a provider behind the call.
Wait retryAfterMs when the error carries it, then retry. The TypeScript client does this once when created with retryRateLimited (waits up to 5 seconds by default).
DRIVER_ERROR
HTTP 502.
A driver, a node command or a site agent failed, or answered with something compute cannot trust: a BMC operation that failed, an unattested result, or firmware and BIOS settings that did not take.
The message names the step; details may carry the driver's own code (driverCode).
PROVIDER_ERROR
HTTP 502.
A VPS or bare-metal provider's API refused or failed the call.
The message carries the provider's reason. Background work such as instance bootstraps retries it; a direct call does not.
INVALID_CONFIG
HTTP 500.
The deployment is configured wrongly, for example an option out of range or a module that needs another one.
This describes the server, not the request, so it answers 500. Fix the configuration and restart.
NOT_IMPLEMENTED
HTTP 501.
The method or feature is planned for a later phase and not built in this release.
details names the feature and the phase that delivers it. See the roadmap.
CSRF_REJECTED
HTTP 403.
A POST lacks content-type: application/json or the X-Better-Compute: 1 header, or a cookie-authenticated request has no Origin.
Browsers cannot send that header cross-site without a preflight, so it stops cross-site request forgery. Clients built on the SDK send it.
UNTRUSTED_ORIGIN
HTTP 403.
A cookie-authenticated request, or a tunnel's WebSocket upgrade, came from an Origin other than the deployment's own.
METHOD_NOT_ALLOWED
HTTP 405.
The HTTP method is not accepted on this path.
API methods are POST only; GET serves /health and /.well-known/better-compute; tunnels need a WebSocket upgrade.
PAYLOAD_TOO_LARGE
HTTP 413.
The request body is larger than the server accepts.
INTERNAL_ERROR
HTTP 500.
An unexpected failure on the server.
The server logs it with the request id (x-request-id); quote that id when reporting it.
MISSING_DEPENDENCY
HTTP 500.
An optional package or service the deployment needs for this call is not installed or not configured, such as PGlite for an embedded database or a remote IAM endpoint.
Like INVALID_CONFIG, it describes the deployment, so it answers 500.
CONFIG_DRIFT
HTTP 409.
The fleet changed after the plan was made, so fleet.apply refused a plan whose planHash no longer matches.
Plan again and review the new plan. --fail-on-drift in the CLI uses the same code. See Fleet configuration.
UNAVAILABLE
HTTP 503.
A dependency the request needs is unreachable for now: the secret store's KMS, a node's gateway, or remote IAM.
Nothing was changed. Retry after retryAfterMs.
TENANT_SUSPENDED
HTTP 403.
The IAM tenant is suspended, so its mutations are refused, or deleted, so every call is refused.
details carries the tenantId and its state. A suspension or deletion also covers the tenant's descendants, and root-tenant callers are exempt. Reads still work in a suspended tenant. See Tenancy.
Better Compute is created by Sean Filimon
Last updated