Docs
Errors
Codes, retry rules, MCP failures.
Failures on JSON ops/proxy return an envelope with
data: nulland anerrorobject. HTTP status matches the failure class.
Envelope error body
{
"data": null,
"meta": {
"schema": "v1",
"request_id": "req_…",
"outcome": "failed",
"cache": "bypass",
"latency_ms": { "hub": 0.3 }
},
"error": {
"code": "rate_limited",
"message": "rate limit exceeded (61/60 in 60s)",
"retryable": true,
"request_id": "req_…",
"retry_after_ms": 1000
}
}
When retry_after_ms is present the response also includes an HTTP Retry-After header (seconds, rounded up). Prefer retry_after_ms in SDKs; use the header for generic HTTP clients.
Catalog
| Code | HTTP | Retry? | Meaning | What to do |
|---|---|---|---|---|
unauthorized |
401 | no | Missing/invalid Bearer key | Check Authorization; republish LKG after mint/rotate |
forbidden_scope |
403 | no | Key lacks connector/op/category scope | Widen scopes; republish LKG |
forbidden_role |
403 | no | Key role cannot access this route | Use an app/admin key with the right role |
forbidden_ip |
403 | no | Caller IP not on allowlist | Update key IP policy |
forbidden_plan |
403 | no | Plan cannot use this thinking tier | Upgrade plan or pick a lower tier (anybody / thinker / expert) |
forbidden_model |
403 | no | Model not allowed for this key | Request model allowlist change or pick an allowed model |
invalid_input |
400 | no | Bad path/body/unknown op | Fix request; include Idempotency-Key when required |
invalid_request |
400 | no | Malformed form / request parse | Fix encoding / content-type |
body_too_large |
413 | no | Request body over limit | Shrink payload |
pii_blocked |
400 | no | Payload failed PII policy | Redact fields or change data policy |
region_unavailable |
403 | no | No egress for requested region | Pick a supported SB-Region / cell |
rate_limited |
429 | yes | RPM/TPM, lane contention, or anomaly throttle | Wait retry_after_ms (default 1000); reuse Idempotency-Key |
budget_exceeded |
402 | no\* | Soft/hard spend cap blocked admit | Raise tenant budget or wait; backoff uses retry_after_ms (default 60000) so clients do not hammer |
insufficient_credits |
402 | no\* | Prepaid balance empty | Top up credits; same backoff hint as budget |
dedupe_unavailable |
503 | yes | Hub idempotency store down | Retry with the same Idempotency-Key after retry_after_ms |
provider_down |
502/503 | yes | Upstream/provider failure or empty ladder | Fail over cells; retry with same idempotency key |
approval_denied |
403 | no | Operator denied the side-effect tool call | Do not retry; change the action or get approval |
approval_pending |
200 (MCP) | poll | Side-effect tool awaiting approve/deny | Poll approval status; do not re-issue a new idempotency key |
unavailable |
503 | no | Feature not loaded (e.g. thinking tiers) | Operator must load config; retry later |
internal |
500 | sometimes | Unexpected hub error | Retry only if retryable: true |
\*Not auto-retryable for spend — raising budget/credits is required — but retry_after_ms + Retry-After are still set so clients back off cleanly.
Retry rules for agents and SDKs
- Honor
error.retry_after_ms/Retry-Afteron 429, budget, lane, and retryable 5xx. - Retry connection errors and 503 across the cell endpoint list; reuse the same
Idempotency-Key. - Do not retry non-idempotent 4xx (
forbidden_scope,invalid_input,unauthorized,pii_blocked,forbidden_plan,forbidden_model). @switchboard/sdkSwitchboardClientalready fails over ordered endpoints on connection/503.
MCP errors
MCP uses JSON-RPC error objects (code integer + message), not the HTTP envelope, on the MCP response body. HTTP auth failures may still return the envelope. Approval flows surface approval_pending / approval_denied in the MCP result / error string.