Docs
Errors
Handle Scry HTTP error envelopes, status codes, retry guidance, and long-query failures.
Error shape
Application errors emitted by Scry handlers use JSON shaped as {"error":{"code":"...","message":"...","details":...}}. The details field is omitted when the error has no structured detail. Retryable responses can include the Retry-After header. Framework-level JSON parsing failures can use a different response shape.
{
"error": {
"code": "invalid_request",
"message": "Missing Content-Type. Use text/plain, text/sql, application/sql, or application/json."
}
} Status codes
The routes documented here return the following status and code families.
| Status | Codes and meaning |
|---|---|
| 400 | `invalid_request` for unsupported content types, invalid SQL, or endpoint validation failures; `query_exposure_exhausted` when raw machine burden crosses `x-scry-budget` (MCP `budget_nanodollars`), with `burden_nanodollars`, `elapsed_ms`, and `kill_source` in details and nothing partial returned. JSON extractor failures can use a framework response. |
| 401 | `unauthorized` when the bearer credential is missing, malformed, or invalid. |
| 402 | `insufficient_credits` or the query payment challenge when funds or payment authorization are required. |
| 403 | `forbidden` when account state or key scope denies the request. |
| 405 | `method_not_allowed`; the SQL route also returns `Allow: POST`. |
| 408 | `request_timeout` or `runtime_deadline_exceeded` when execution exceeds its deadline, or when the forecast alone does (the message then says the statement was refused before execution). The deadline is 15 s with no header; `x-scry-max-seconds` raises it to at most 2000 s under an account key (a larger value is clamped, and the envelope reports what ran as `authorized_seconds`), 60 s on the x402 lane, and 1800 s for a program. |
| 409 | `intent_settled` when an `idempotency-key` (MCP `idempotency_key`) identifies a statement that already settled: details include `query_id`, `row_count`, `settled_at`, and `spend_nanodollars`, and nothing runs again. |
| 429 | `rate_limited`, `query_capacity_exhausted`, or `priced_out` (posted congestion multiplier above your `x-scry-max-multiplier` ceiling); inspect `Retry-After`. |
| 500 | `internal_error` when the server cannot complete internal work. |
| 503 | `service_unavailable` when an endpoint, provider set, admission channel, or dependency is unavailable. |
Gateway errors
A 502 or 504 with no JSON error envelope comes from the gateway in front of the application, not from a Scry handler: the SQL is not at fault and rewriting it changes nothing, but whether the statement ran is unknown — a 502 is a refused connection or a broken reply, a 504 a reply that did not arrive in time, and either can land after the engine began work. Name the intent (idempotency-key header, MCP idempotency_key) and the retry answers for itself: if the first attempt settled, the door returns intent_settled with its query_id, row_count, and spend_nanodollars instead of running it again; if it never ran, it runs once. https://status.scry.io reports disruptions independently of the API.
Long queries
After the synchronous grace period, the SQL route can begin a 200 application/json stream and send whitespace while execution continues. A later failure then appears as the same JSON error object inside that 200 response because the HTTP status has already been sent. Check the body for error before treating a long-query response as a result.
Related docs
Want even more out of Scry? Think there's something we can do? Reach out with a request or an offer at hi@scry.io.