Skip to main content

Error handling

All Talos API errors follow the google.rpc.Status shape. This guide covers the error response structure, common error codes, and retry strategies.

Error response format

Every non-2xx response uses the google.rpc.Status envelope:

{
"code": 5,
"message": "API key not found",
"details": [
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "API_KEY_NOT_FOUND",
"domain": "talos.ory.sh",
"metadata": {
"key_id": "01J9X7…"
}
}
]
}
FieldDescription
codeCanonical gRPC status code (integer 0-16).
messageHuman-readable summary. Suitable for logging, not for end-user display.
detailsOptional list of typed error details. ErrorInfo carries the machine-readable reason.

The HTTP status code is set from the canonical gRPC-to-HTTP mapping. For example, code 5 (NOT_FOUND) returns HTTP 404; code 7 (PERMISSION_DENIED) returns HTTP 403.

Reading the reason

The stable, machine-readable identifier is details[*].reason on the ErrorInfo detail. Match on reason — never on message, which can change between releases.

REASON=$(echo "$RESPONSE" | jq -er '.details[]? | select(."@type" | endswith("ErrorInfo")).reason')

Verification errors

The verify endpoint (POST /v2alpha1/admin/apiKeys:verify) is the one exception. A verification failure is part of the normal verification result, not a transport-level error, so it returns 200 OK with is_active: false and a structured error code:

{
"is_active": false,
"error_code": "VERIFICATION_ERROR_REVOKED",
"error_message": "The API key has been revoked."
}

Treat the response as successful; act on is_active and error_code. Only fall back to the google.rpc.Status handling above when the HTTP status is not 2xx (for example, the verify request itself was malformed).

For the complete list of verification error codes (VERIFICATION_ERROR_*), see the error codes reference.

HTTP status codes

For the complete list of HTTP status codes and reasons, see the error codes reference.

Key categories:

  • 4xx errors: Client errors (bad request, not found, conflict). Fix the request before retrying.
  • 5xx errors: Server errors. Retry with exponential backoff.

Retry strategy

Safe to retry

  • UNAVAILABLE (HTTP 503) — the server is temporarily overloaded. Retry with exponential backoff.
  • DEADLINE_EXCEEDED (HTTP 504) — the request timed out. Retry with backoff.
  • Network errors — connection refused, DNS failure, etc. Retry with backoff.

Not safe to retry without an idempotency key

  • ALREADY_EXISTS (HTTP 409) — the resource already exists. Read the existing resource and reconcile.
  • INVALID_ARGUMENT (HTTP 400) / FAILED_PRECONDITION (HTTP 400) — fix the request before retrying.

Idempotency key

When issuing API keys, include request_id in the request body. This field is stored on the key for client-side deduplication:

# Note: request_id is only available via the HTTP API.
talos keys issue "my-service" --actor user_1 -e "$TALOS_URL"

The request_id is recorded in the key's metadata. The server does not enforce server-side idempotent replay — sending the same request_id twice creates two keys.

attempt 1: wait 100ms
attempt 2: wait 200ms
attempt 3: wait 400ms
attempt 4: wait 800ms
attempt 5: wait 1600ms (give up after this)

Add jitter (random 0-50% of the wait time) to avoid thundering-herd effects.

Next steps