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 fullWait Retry-After; safe to retry
429rate_limit_exceededconcurrency limited, retry after 2sAnother turn is running on this grant, or the service is at its shared ceilingReduce parallelism, then retry
429insufficient_quotausage limit reachedThe month's allowance is spentWait for the month, or the account changes plan
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.