Skip to content

API Errors

Varity Team Core Contributors Updated August 2026

Every error from the Varity platform API (https://varity.app/api) returns the same JSON envelope. Program against code and retryable; show message and action to humans.

{
"code": "authentication_required",
"message": "A Bearer API key is required.",
"action": "Send Authorization: Bearer <VARITY_API_KEY>.",
"retryable": false,
"docs_url": "https://docs.varity.so/api/errors#authentication_required",
"correlation_id": "3e0dbab4-bb81-4e95-a9b6-baefeff0e52e"
}
FieldTypeAlways presentMeaning
codestringyesStable machine-readable identifier. Branch on this, never on message.
messagestringyesHuman-readable statement of what went wrong.
actionstringyesWhat the caller should actually do next.
retryablebooleanyesWhether retrying the same request can succeed.
docs_urlstring (uri)yesDeep link to this page, anchored at the code.
fieldstringnoThe offending request field, on validation failures.
correlation_idstringnoRequest correlation identifier. See below.

Caller Fault, Platform Fault, Provider Fault

Section titled “Caller Fault, Platform Fault, Provider Fault”

The single most useful thing about a Varity error is whose problem it is. The HTTP status tells you the class; retryable tells you whether to try again.

Caller fault: 4xx, retryable: false. Your request is wrong. Retrying the identical request produces the identical error. Fix the request, the credential, or the account state. This covers bad input (400, 422), authentication (401), scope (403), missing resources (404), and conflicts (409).

Platform fault: 5xx, retryable: true. A Varity component could not answer. The request may still be valid. Retry with exponential backoff and jitter.

Provider fault: 5xx and 409, retryable: true. A downstream compute or inference supplier was unavailable or out of capacity. Varity does not expose supplier identity in the public contract; you see it as an unavailable-or-capacity code. Retry with backoff, and for capacity errors consider a different profile.

The reliable rule: retry when and only when retryable is true. Do not infer retryability from the status code alone. 409 gpu_capacity_unavailable is retryable while 409 idempotency_key_reused is not.

Every code below is emitted by the live gateway. Statuses and retryable values are as implemented.

CodeHTTPRetryableCause
authentication_required401noNo Authorization: Bearer header was sent.
invalid_api_key401noThe API key is invalid or revoked.
api_key_owner_unbound401noThe key is not bound to an owner account.
per_user_api_key_required401noThe route requires a per-user API key.
credential_scope_violation403noThe credential is valid but not scoped to this product or resource.
portal_session_required403noThe route requires a Developer Portal session, not an API key.
api_key_auth_unavailable503yesThe key authentication service could not be reached.
api_key_verification_unavailable502/503yesKey verification is temporarily unavailable.
CodeHTTPRetryableCause
invalid_env400noMalformed environment-variable update payload.
invalid_idempotency_key400noThe key is not [A-Za-z0-9._:-]{8,128}.
invalid_run_id400noMalformed run identifier.
invalid_webhook_url400noThe webhook URL is not a public HTTPS URL.
invalid_webhook_id400noMalformed webhook identifier.
invalid_api_key_id400noMalformed API key identifier.
invalid_inference_key_request400noMalformed AI Gateway key request.
invalid_ai_attachment_request400noMalformed AI Gateway attachment request.
preset_unavailable400noThe requested resource preset cannot be served.
accelerator_profile_invalid422noThe accelerator profile selection is not valid.
invalid_accelerator_quote_request422noThe GPU quote request failed validation.
machine_request_invalid422noThe machine create request failed validation.
machine_quote_request_invalid422noThe machine quote request failed validation.
deployment_profile_scope_invalid422noworkload and execution_class do not form a valid scope.
deployment_profile_contract_invalid422noThe requested profile contract version is not valid.
unsupported_deployment_request400/422noThe deployment request shape is not supported.

Validation errors may carry a field naming the offending property.

CodeHTTPRetryableCause
not_found404noThe resource does not exist or is not owned by you.
deployment_not_found404noNo such deployment for this owner.
deployment_run_not_found404noNo such deployment run.
template_not_found404noNo such template.
webhook_not_found404noNo such webhook.
idempotency_key_reused409noThe key was reused with a different request body.
legacy_deployment_retired409noThe deployment predates the current runtime contract.
legacy_run_retired410noThe run record is retired and permanently gone.
restart_unavailable409/501/503variesRestart is not possible for this deployment. 503 is retryable; 409 and 501 are not.
template_not_certified409/503variesThe template is not certified, or certification could not be checked. 503 is retryable; 409 is not.
CodeHTTPRetryableCause
billing_or_credit_required402noNo payment method and no active starter credit.
gpu_capacity_unavailable409yesNo accelerator capacity right now for the requested profile.

gpu_capacity_unavailable is the one common 409 you should retry. Capacity is a moving target; back off and retry, or quote a different profile.

All of these are retryable: true. They mean a Varity subsystem or a downstream provider did not answer, not that your request was wrong.

CodeHTTPCause
deploy_service_unavailable502/503The deploy service could not be reached.
deploy_status_unavailable502Run status could not be read.
deployment_read_unavailable502The deployment record could not be read.
logs_unavailable502/503The log source could not be reached.
credit_state_unavailable503Credit state could not be read.
billing_summary_unavailable503The billing summary could not be read.
webhook_state_unavailable503Webhook state could not be read.
custom_domain_unavailable503Custom-domain state could not be read.
gpu_quote_unavailable503A GPU quote could not be issued.
idempotency_unavailable503The idempotency store could not be reached.
api_key_management_unavailable502/503Key management could not be reached.
ai_attachment_unavailable502AI Gateway attachment state could not be read.
env_update_failed5xx (varies)The environment update did not apply.
redeploy_failed5xx (varies)The redeploy did not start.
deployment_delete_failed5xx (varies)The delete did not complete.
CodeHTTPRetryableCause
rate_limited429yesAn account rate-limit budget is exhausted.

Read the Retry-After header and wait. See Rate Limits for the budgets and headers.

Responses That Do Not Use The Standard Envelope

Section titled “Responses That Do Not Use The Standard Envelope”

A few routes predate the envelope and answer with their own shape. They still carry a code, but not retryable or docs_url. Parse defensively: read code from the top level, and fall back to error.code and detail.code.

GET /api/deployments/{id}/health-history on failure returns 503 with the code nested under error:

{ "error": { "code": "history_unavailable", "message": "Runtime history is temporarily unavailable.", "retryable": true } }

An unrepresentable deploy request returns 422 with the code nested under detail:

{ "detail": { "stage": "acceptance", "code": "unsupported_deployment_request", "message": "This deploy request cannot be represented by the durable operation engine." } }

An unknown pricing profile or preset returns 400 and fails closed by returning the valid menu, using error in place of message:

{ "code": "profile_unknown", "error": "Unknown pricing profile \"bogus\"", "profiles": ["web-app"], "presets": ["starter"] }

The preset_unknown code follows the same shape. All of these are caller-fault except history_unavailable, which is retryable.

These codes are real and you will see them, but they are not in the table above because they do not use the envelope:

codeHTTPshaperetryable
profile_unknown400top-level code + error, plus the valid profiles and presets menuno
preset_unknown400top-level code + error, plus the valid presets menuno
history_unavailable503nested under erroryes
unsupported_deployment_request422nested under detailno

Log, event, and health reads return 200 with a complete flag rather than failing when the live source is briefly unavailable. When complete is false the window may be partial and a warning_code is set (logs_incomplete, events_incomplete). This is a successful response describing an incomplete window. Retry the read for a complete one; do not treat it as a failure.

Every error carries a correlation_id when one is available. It is the single most useful thing you can give support.

Supply your own. Send an X-Request-Id header and the platform echoes that exact value back as correlation_id:

Terminal window
curl https://varity.app/api/credits \
-H "Authorization: Bearer $VARITY_API_KEY" \
-H "X-Request-Id: my-service-20260822-0001"
{ "code": "authentication_required", "correlation_id": "my-service-20260822-0001" }

If you do not send one, the platform assigns a UUID. Log correlation_id on every non-2xx response alongside your own request context. When you open a support request, include it. With it, a failure is traceable end to end; without it, it usually is not.

import time, random, requests
def call(method, url, **kw):
for attempt in range(5):
r = requests.request(method, url, **kw)
if r.status_code < 400:
return r.json()
err = r.json()
if not err.get("retryable"):
raise RuntimeError(
f"{err['code']}: {err['message']} "
f"({err['action']}) correlation_id={err.get('correlation_id')}"
)
# Honor the server's window when it gives one.
wait = float(r.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait + random.uniform(0, 0.5))
raise RuntimeError("retries exhausted")