Error envelope
Every Cloven error response, on every surface, follows one shape. Branch on code, surface message to humans, log requestId for support.
{
"error": {
"code": "pack_access_denied",
"message": "your key does not have access to pack 'ai'; upgrade to unlock all packs",
"requestId": "req_01HXY8AB..."
}
}The MCP surface wraps the same payload inside the tool error contract:
{
"isError": true,
"content": [
{ "type": "text", "text": "pack_access_denied: your key does not have access to pack 'ai'..." }
]
}The SDK throws a typed ClovenApiError carrying code, message, requestId, and status.
Status codes
| Status | Code | Cause | Resolution |
|---|---|---|---|
| 400 | missing_query | /v1/search called without q | Supply a non-empty q param. |
| 400 | missing_at | /v1/snapshot called without at | Supply an ISO8601 at. |
| 400 | invalid_at | at is not a parseable ISO8601 | Use new Date().toISOString() or equivalent. |
| 400 | missing_ref | /v1/cite called without ref | Supply integer ref ≥ 1. |
| 400 | invalid_ref | ref is not a positive integer | Supply integer ref ≥ 1. |
| 400 | invalid_payment | x-payment header malformed | Re-encode the proof; confirm base64. |
| 401 | invalid_token | Bearer/SSE token failed validation | Re-issue from /console. |
| 401 | missing_token | SSE called without token query param | Add ?token=cv_…. |
| 401 | key_revoked | Key was revoked since last cache refresh | Issue a replacement. |
| 402 | payment_required | No auth + no payment proof | Pay USDC per x402 spec. |
| 402 | tx_already_settled | Same txHash reused | Pay a fresh tx for a fresh request. |
| 402 | tx_too_old | Tx confirmed > 10 min ago | Pay a fresh tx. |
| 402 | insufficient_payment | Tx amount below quote | Re-quote, pay the full amount. |
| 403 | pack_access_denied | Key tier doesn't include the requested pack | Upgrade tier or change pack. |
| 404 | pack_not_found | Unknown pack id | Check cloven://packs or pack catalog. |
| 404 | snapshot_not_found | No snapshot at the requested minute | Try a nearby minute; older snapshots require Pro+. |
| 404 | citation_not_found | ref out of range for this pack | Check pack.sources.length. |
| 429 | quota_exhausted | Tier quota exceeded | Wait for window roll or upgrade. Retry-After header included. |
| 500 | internal_error | Unhandled — engineering paged | Retry with backoff; report requestId. |
| 503 | state_unavailable | First pulse hasn't landed (cold pack) | Retry in 30s. |
| 503 | brief_unavailable | Pack hasn't produced first brief | Retry in 60s. |
Codes are stable
code values are part of the public contract. They will not change without a major version bump. New codes may be added — branch on known codes, default to "log + retry" for unknown ones.
requestId
Every response — success or error — carries X-Request-Id. On errors it's also inlined into the error envelope. Pass it to support and we can pull the full trace (route, pack, source health snapshot, Groq token counts) in one query.
requestId is a ULID — sortable by creation time, safe to log, safe to surface to end users.
Error voice
Cloven error messages are concrete, technical, and never blame the user when the cause is on our side. Read them — they tell you the fix.
- Good:
"your key does not have access to pack 'ai'; upgrade to unlock all packs". - Good:
"no snapshot found for pack 'crypto' at 2026-05-23T03:04 (snapshots older than 24h require a Pro+ subscription and Postgres lookup)". - Bad (we don't do this):
"Error occurred. Please try again.".
If you ever see a vague message, file an issue — that's a bug in our error catalog.
Retries
Cloven endpoints are idempotent (the cite endpoint accepts both GET and POST; POST is for symmetry, not state mutation). Safe to retry on 5xx and 429 with exponential backoff. Do not retry on 400/401/403/404 — those errors indicate a request shape problem that retrying won't fix.
x402 retries are special: a 402 means "pay first, then retry". The SDK handles this transparently in wallet mode. If you're rolling your own client, treat 402 as the start of a payment dance, not as a retry condition.
Rate limit detail
The 429 response carries Retry-After: <seconds> indicating when your quota window rolls. The sliding window is per-key. Multiple keys on the same account each have their own window. Plan around the window if you batch — the cleanest pattern is to back off until Retry-After, then resume at half the previous rate.