Overview

Error handling

Diagnose API failures, retry safely, and collect the evidence needed to debug a request.

Use the HTTP status and response body to decide whether an operation succeeded. Budget headers describe admission, not the result of the endpoint: an allowed request can still fail validation or encounter a server error.

The coworld and softmax CLIs apply this page for you on Observatory API calls: a failed request prints the status, the server’s detail, a next step, the request id, and the documentation link instead of a traceback. The browser sign-in exchange (softmax exchange-code) reports its own shorter failure message.

Diagnose the response

ResponseNext step
400 or 422Fix the request using the endpoint schema and validation details. Do not retry unchanged input.
401Check the Bearer token and active identity. Run uv run softmax status; sign in again if needed.
403Check ownership, identity type, and required permissions. Repeated login does not grant access.
402Inspect the credit or payment requirement in the body. Waiting for a rate-limit refill does not add credits.
404Check the API base URL, route, and resource identifier. Some resources may not be visible to your identity.
409Inspect the conflict and refresh the resource before choosing whether to retry.
429Inspect the error type and Retry-After; reduce concurrency and pace requests.
500, 502, 503, or 504Retry eligible operations with bounded backoff. A failed response does not prove a write was not committed.

The base URL is https://softmax.com/api/observatory, including /observatory. An HTML 404 often means the request reached the website instead of the API. Inspect Content-Type before trying to parse an error as JSON.

See Authentication and identities for user and player credentials.

Distinguish throttling from other failures

Shared API budget rejections use detail.type: "api_rate_limit_exceeded" when enforcement is active. These requests do not run the endpoint handler. Follow Rate limits for the response headers and retry example.

Other protections can also return 429. An operation-specific write limit uses detail.type: "write_rate_limit_exceeded". Concurrent-request limits may return a text detail instead. Do not assume every error body has the same shape, or that every 429 came from the shared minute/hour buckets. Branch on a documented stable type, not the human-readable message.

Retry safely

Honor Retry-After when supplied. It is a minimum delay, not a reservation: another worker sharing your budget may spend the available credits first. Add jitter, bound attempts and total elapsed time, and avoid retries at several nested layers.

Reads are normally safe to repeat. For writes, use the endpoint’s documented idempotency key or deduplication behavior; reuse the same key and payload on a retry. Do not invent an Idempotency-Key header for an endpoint that does not support one. After an ambiguous timeout, inspect the operation’s status before submitting a new operation.

Do not retry a successful response because X-RateLimit-Outcome says would_reject or unavailable. Those outcomes allow the handler to run. Repeating a successful write can duplicate work.

Collect a useful debugging report

Capture the request method, route, UTC timestamp, HTTP status, Content-Type, response body, X-Request-ID, and any Retry-After or X-RateLimit-* headers. Include whether the caller was a user, player, or service, and whether several workers shared the identity.

Keep credentials, cookies, private request payloads, and personal data out of shared reports. Never include the Authorization header. Contact Softmax through the contact page with the request ID and a redacted reproduction.