In 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

Statustypecode or messageCauseFix
400invalid_request_errorrequest body must be a JSON objectThe body was not an objectSend {"model": "miles", "messages": [...]}
400invalid_request_errornames the fieldA role other than system/user/assistant, an image or audio part, a malformed messages entryRemove the part; only text content is read
401invalid_request_errorinvalid_api_keyThe key was revoked or never existed, or the OAuth token expiredMint a new key, or refresh the token
401invalid_request_errorinvalid_tokenThe bearer is not a Miles credential, or lacks miles:chatSend a personal key or a miles:chat token
403invalid_request_errorconnections_pausedThe account paused its AI app connectionsThe user turns them back on at Settings → Connected AI Apps; nothing to re-mint
413invalid_request_errorrequest too largeThe body exceeded the size capTrim history; only the last 26 messages are read anyway
429rate_limit_exceededrate limited, retry after NsThe per-minute window is full. GET /v1/models has its own such window, separate from the one completions useWait Retry-After; safe to retry
429rate_limit_exceededconcurrency limited, retry after 2sAnother turn is running on this grant or on another grant of the same account, or the service is at its shared ceilingReduce parallelism, then retry
429rate_limit_exceededYou've reached today's usage limit. Please try again later.The account's daily spend allowance is full. This is not the per-minute window and does not clear in a minuteWait Retry-After, which is an hour here; the allowance resets at midnight Pacific
429insufficient_quotaYou've used this month's budget. It resets on …The account's monthly budget is spentWait Retry-After, which is the seconds until the reset date and can be days, so do not retry on the usual 429 schedule. Members get a much larger monthly budget
500server_errorthe pipeline failed to answerMiles could not produce an answerRetry once; if it repeats, the outage is on Miles' side
504timeout_errorthe request took too long to answerA non-streaming turn passed 90 secondsRetry 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. Every type and code this endpoint puts in the envelope, and every MCP and tool refusal code, has its own anchor on the Error codes reference. The one exception is the invalid_token above, which is the RFC 6750 bearer error the WWW-Authenticate header carries rather than a Miles refusal code; it is documented here and nowhere else.