Error Handling
BlockRun uses standard HTTP status codes and returns detailed error information.
HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad Request - invalid parameters, unknown model, oversized body, or an upstream rejection of one of your parameters (surfaced as 400, never 500) |
| 402 | Payment Required - sign and retry with payment; or a payment that failed verification (carries a code, see below) |
| 429 | Rate limited - upstream capacity for that model, or the per-IP free-tier limit; honor Retry-After |
| 503 | Model unavailable / upstream configuration problem - retry or fail over |
| 500 | Server Error - something unexpected went wrong |
| 504 | Upstream timed out (120s) - nothing settled; retry |
Error Response Format
Gateway errors use the OpenAI envelope, with message and code mirrored at the top level for older clients. debug appears only when the fault is yours to fix — a 4xx such as an upstream rejecting one of your parameters — and then carries the upstream's description of it, redacted: credentials, URLs, deployment and account identifiers, and any routing infrastructure we do not sell under its own name are removed. A 429 or 5xx never carries upstream text: it describes our capacity or an upstream fault, which you cannot act on, so the body has just code and message (we keep the full text in our logs — quote the x-blockrun-gateway-request-id response header when you report one).
{
"error": {
"message": "Invalid request parameter — Message @bc1max on Telegram for help.",
"type": "invalid_request_error",
"code": "INVALID_PARAMETER",
"param": null
},
"message": "Message @bc1max on Telegram for help.",
"code": "INVALID_PARAMETER",
"debug": "<the upstream's description of the rejected parameter, redacted>"
}
error.type is one of invalid_request_error, rate_limit_error, api_error, payment_required. The Anthropic-compatible /v1/messages endpoint answers in Anthropic's {"type":"error","error":{"type","message"}} shape and /v1/responses in OpenAI's {"error":{"message","type","param","code"}} shape instead.
Error codes
code | Status | Meaning |
|---|---|---|
INVALID_JSON | 400 | Body is not valid JSON |
INVALID_REQUEST_BODY | 400 | Schema validation failed; details lists the offending paths |
EMPTY_CONVERSATION | 400 | messages had only system entries |
INVALID_PARAMETER | 400 | Upstream rejected one of your sampling/tool parameters |
CONTEXT_LENGTH_EXCEEDED | 400 | Prompt does not fit the model's context window |
INVALID_IMAGE_URL | 400 | An image_url could not be fetched |
REQUEST_TOO_LARGE | 400 | Body over the size limit |
CONTENT_FILTERED | 400 | Upstream safety filter blocked the request |
STREAM_UNSUPPORTED | 400 | stream: true on a model that only serves non-streaming |
REASONING_FORMAT_ERROR, TOOL_ID_FORMAT_ERROR, INVALID_REQUEST | 400 | Message-history shape problems |
PAYMENT_INVALID, PAYMENT_UNFUNDED, PAYMENT_BLOCKHASH_STALE, PAYMENT_REPLAY | 402 | Payment verification failed — see below |
RATE_LIMITED | 429 | Upstream capacity for that model exhausted; Retry-After + X-RateLimit-Source set |
FREE_TIER_RATE_LIMITED | 429 | Per-IP free-tier limit (30/min, 300/hour) |
STREAM_FAILED, FREE_MODEL_FAILED | 429 | Free-model capacity exhausted; Retry-After: 30 |
MODEL_UNAVAILABLE | 503 | Upstream reports the model missing, overloaded, or at capacity |
PROVIDER_CONFIG_ERROR | 503 | Upstream credential/quota problem on our side |
TIMEOUT | 504 | Upstream did not answer within 120s |
INTERNAL_ERROR | 500 | Anything unclassified |
Errors after a stream has started
Once a stream: true response has begun, the status is already 200, so a failure arrives as a final SSE error event instead. Its message is fixed text ending in a code, never upstream text:
| Code | Meaning |
|---|---|
(code: deadline) | The response ran past the gateway's time limit and was cut off |
(code: stall) | The upstream stopped sending data and the stream was cut off |
(code: rate_limited) | The upstream hit its rate limit mid-response; retry shortly |
(code: upstream_error) | The upstream failed before completing the response |
Upstream rejected the request (4xx): … | The upstream refused one of your parameters; the redacted reason follows, as with debug above |
Billing for a cut-off stream follows the settlement rules for that endpoint: see the X-Payment-* response headers.
Common Errors
400 - Invalid Request
{
"error": {"message": "Invalid request body", "type": "invalid_request_error", "code": "INVALID_REQUEST_BODY", "param": null},
"message": "Invalid request body",
"code": "INVALID_REQUEST_BODY",
"details": [
{"path": ["model"], "message": "Required"}
]
}
An unknown model is a plain-string error that suggests live IDs:
{"error": "Unknown model: openai/gpt-4. Try one of: openai/gpt-5.6-sol, …. Full list: GET /v1/models"}
Causes:
- Missing required fields (
model,messages) - Invalid model ID
- Malformed JSON
- A parameter the upstream model rejects (
INVALID_PARAMETER) — the gateway surfaces these as 400, not 500
402 - Payment Required
{
"x402Version": 2,
"accepts": [{"scheme": "exact", "network": "eip155:8453", "amount": "246850", "asset": "0x8335…", "payTo": "0x…", "maxTimeoutSeconds": 300}],
"error": "Payment Required",
"message": "This endpoint requires x402 payment",
"price": {"amount": "0.246850", "currency": "USD"},
"paymentInfo": {"network": "base", "asset": "USDC", "x402Version": 2}
}
The signed requirements travel in the X-Payment-Required / PAYMENT-REQUIRED headers (and WWW-Authenticate: X402 requirements="…"), and — since 2026-08-30 — are mirrored at the top level of the JSON body too (x402Version, accepts), byte-identical to the decoded header. This is for v1-era x402 clients (early x402-fetch/x402-axios and third-party wrappers) that only parse the body and silently fail to auto-pay when there's no top-level accepts. price.amount equals the signed amount, including the flat $0.001 transaction fee. On chat, an exact payment is quoted and settled at input plus your full max_tokens as output (8192 when unset); see How the quote is computed.
A 402 Payment Required is part of the normal x402 flow — the gateway is quoting a price. Sign and retry with payment and the SDKs handle this round-trip automatically.
402 - Payment Rejected
A PAYMENT-SIGNATURE that fails verification is answered with 402 and a machine-readable code (since 2026-08-26, on every BlockRun-native paid endpoint):
{
"error": "Payment verification failed",
"code": "PAYMENT_UNFUNDED",
"message": "The payment authorization could not be executed on-chain. The usual cause is an insufficient USDC balance on Base for the quoted amount — …",
"debug": "<facilitator reason>",
"payer": "0x…"
}
code | Meaning | Fix |
|---|---|---|
PAYMENT_UNFUNDED | Transfer simulation reverted — usually insufficient USDC (an authorization outside its validAfter/validBefore window reverts the same way) | Fund the wallet; re-sign if your clock is off |
PAYMENT_BLOCKHASH_STALE | Solana-signed payment pinned to an expired blockhash. Nothing was charged | Re-sign against a current blockhash and resend |
PAYMENT_REPLAY | That authorization nonce was already used | Sign a fresh authorization per request. (On image endpoints a replay of a paid-but-lost response returns the job you already paid for instead) |
PAYMENT_INVALID | Any other verification failure — bad signature, wrong network or asset, malformed payload | Sign exactly the requirements from the 402 header |
Verification always runs before settlement, so none of these charged you. On the Solana gateway PAYMENT_VERIFICATION_UNAVAILABLE means the facilitator was unreachable — retry the same signed payment.
429 - Rate Limited
{
"error": {"message": "Rate limited — … retry after 60s, or fail over to a same-tier model on a different provider.", "type": "rate_limit_error", "code": "RATE_LIMITED", "param": null},
"code": "RATE_LIMITED",
"source": "openai",
"retry_after_seconds": 60
}
Headers: Retry-After: 60, X-RateLimit-Source: <model family>. Free models add FREE_TIER_RATE_LIMITED with X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset. See Rate Limits.
503 - Model Unavailable
{
"error": {"message": "Model unavailable — Message @bc1max on Telegram for help.", "type": "api_error", "code": "MODEL_UNAVAILABLE", "param": null},
"code": "MODEL_UNAVAILABLE"
}
Retry, or fail over to a same-tier model from a different family. Not settled.
500 - Server Error
{
"error": {"message": "Unexpected error — Message @bc1max on Telegram for help.", "type": "api_error", "code": "INTERNAL_ERROR", "param": null},
"message": "Message @bc1max on Telegram for help.",
"code": "INTERNAL_ERROR"
}
Causes:
- Unclassified upstream error
- Temporary service issue
Settlement happens only after a successful upstream response, so a 500/503/504 never charges you.
SDK Error Classes
Troubleshooting
"Payment verification failed"
- Read the
code:PAYMENT_UNFUNDED→ top up USDC on Base (or on Solana forsol.blockrun.ai) PAYMENT_REPLAY→ your client reused a nonce; sign a fresh authorization per requestPAYMENT_BLOCKHASH_STALE→ re-sign; the Solana blockhash expired between signing and sendingPAYMENT_INVALID→ confirm you signed the exactaccepts[0]entry from the header (network, asset, amount,payTo) and that the wallet is on the right network
"Unknown model"
- Check the model ID matches exactly (e.g.,
openai/gpt-5.5) - See available models for valid IDs
"Timeout"
- Increase the timeout in client options
- Try a faster model (e.g.,
gpt-5.4-miniinstead of a-proreasoning tier), or lowermax_tokens— the gateway's own upstream timeout is 120s per call
"Network error"
- Check your internet connection
- Verify
blockrun.ai/apiis accessible