--- title: "Errors" description: "Standard error shape, common status codes, and retry strategies." --- ## Error shape All error responses are JSON with `Content-Type: application/json`: ```json { "error": "brief_not_found" } ``` Some endpoints include additional fields: ```json { "error": "invalid_request", "error_description": "iso2 must be a 2-letter uppercase country code", "field": "iso2" } ``` OAuth endpoints follow [RFC 6749 §5.2](https://datatracker.ietf.org/doc/html/rfc6749#section-5.2) error codes: `invalid_request`, `invalid_client`, `invalid_grant`, `unsupported_grant_type`, `invalid_scope`. ## Status codes | Code | Meaning | Retry? | |------|---------|--------| | `200` | OK | — | | `202` | Accepted — job enqueued. Poll for terminal status. | — | | `304` | Not Modified (conditional cache hit) | — | | `400` | Bad request — validation error | No — fix input | | `401` | Missing / invalid auth | No — fix auth | | `403` | Authenticated but not entitled (usually `pro_required`) | No — upgrade | | `404` | Resource / tool / brief / entity not found | No | | `405` | Method not allowed | No | | `409` | Conflict (duplicate webhook registration, etc.) | No | | `413` | Payload too large | No | | `429` | Rate limited | Yes — honor `Retry-After` | | `500` | Server bug | Yes — with backoff, then report | | `502` | Upstream / Convex / Dodo failure | Yes — exponential backoff | | `503` | Service unavailable — missing env or dependency down | Yes — exponential backoff | | `504` | Upstream timeout | Yes — with backoff | ## Common error strings | `error` value | Meaning | |---------------|---------| | `UNAUTHENTICATED` | No valid Clerk JWT or API key. | | `pro_required` | Authenticated, but account is not PRO. | | `invalid_payload` | Body failed schema validation. | | `invalid_date_shape` | Date param not `YYYY-MM-DD`. | | `brief_not_found` | No composed brief for the requested `{userId, issueDate}`. | | `Rate limit exceeded` | 429 fired; honor `Retry-After`. | | `Service temporarily unavailable` | Upstash or another hard dependency missing at request time. | | `service_unavailable` | Signing secret / required env not configured. | | `Failed to enqueue scenario job` | Redis pipeline failure on `/api/scenario/v1/run-scenario`. | ## Retry strategy **Idempotent reads** (`GET`): retry 429/5xx with exponential backoff (1s, 2s, 4s, cap 30s, 5 attempts). Most GET responses are cached at the edge, so the retry usually goes faster. **Writes**: never auto-retry 4xx. For 5xx on writes, inspect: `POST /api/brief/share-url` and `POST /api/v2/shipping/webhooks` are idempotent; `POST /api/scenario/v1/run-scenario` is **not** — it enqueues a new job on each call. Retrying a 5xx on `run-scenario` may double-charge the rate-limit counter. **MCP**: the server returns tool errors in the JSON-RPC result with `isError: true` and a text explanation — those are not HTTP errors. Handle them at the tool-call layer. ## Debugging - Every edge response includes `x-vercel-id` and `x-worldmonitor-deploy` headers — include those when reporting issues. - Sentry alerts forward to [status.worldmonitor.app](https://status.worldmonitor.app/). - `GET /api/health` and `GET /api/seed-health` show per-seed freshness; a stale seed is the most common root cause of unexpected empty payloads.