Chat API
Read this page as MarkdownIn this section: Errors
Errors
Every status the Chat API returns, with its type, code, cause and fix, in the OpenAI error envelope.
The envelope
Every error, including a 413 raised before the body is parsed and an error mid-stream, is JSON in the OpenAI shape:
{"error": {"message": "api key revoked or unknown", "type": "invalid_request_error", "code": "invalid_api_key"}}A 401 also carries WWW-Authenticate: Bearer realm="askmiles", error="invalid_token", so an OAuth-aware SDK's retry logic behaves the same whichever bearer type it sent. A 429 carries Retry-After. Nothing here is cached: every error response is no-store.
The one error that does not wear the envelope is a plain-text 403 from Cloudflare, returned before Miles sees the request when the User-Agent is a stock Python-urllib. Use a client library.
The table
| Status | type | code or message | Cause | Fix |
|---|---|---|---|---|
| 400 | invalid_request_error | request body must be a JSON object | The body was not an object | Send {"model": "miles", "messages": [...]} |
| 400 | invalid_request_error | names the field | A role other than system/user/assistant, an image or audio part, a malformed messages entry | Remove the part; only text content is read |
| 401 | invalid_request_error | invalid_api_key | The key was revoked or never existed, or the OAuth token expired | Mint a new key, or refresh the token |
| 401 | invalid_request_error | invalid_token | The bearer is not a Miles credential, or lacks miles:chat | Send a personal key or a miles:chat token |
| 403 | invalid_request_error | connections_paused | The account paused its AI app connections | The user turns them back on at Settings → Connected AI Apps; nothing to re-mint |
| 413 | invalid_request_error | request too large | The body exceeded the size cap | Trim history; only the last 26 messages are read anyway |
| 429 | rate_limit_exceeded | rate limited, retry after Ns | The per-minute window is full | Wait Retry-After; safe to retry |
| 429 | rate_limit_exceeded | concurrency limited, retry after 2s | Another turn is running on this grant, or the service is at its shared ceiling | Reduce parallelism, then retry |
| 429 | insufficient_quota | usage limit reached | The month's allowance is spent | Wait for the month, or the account changes plan |
| 500 | server_error | the pipeline failed to answer | Miles could not produce an answer | Retry once; if it repeats, the outage is on Miles' side |
| 504 | timeout_error | the request took too long to answer | A non-streaming turn passed 90 seconds | Retry with stream: true, which has no turn timeout |
Retrying a 429 or 504 is safe: the endpoint holds no state, so nothing was half-written. A 400, 401 or 403 will return the same answer until the cause changes.
Mid-stream
If a stream has already opened, an error arrives as one final data: event whose body is the envelope above, followed by data: [DONE]. The HTTP status of the stream is already 200 by then; read the event.
Catalog and MCP errors
The keyless catalog API has its own shape, {"error": "message"}; see Catalog errors. MCP tool refusals arrive as isError results with a structured code; see Quotas and refusals.