Errors
The local API's error envelope and code set.
The envelope
Every local API error is:
{ "error": { "code": "session_not_found", "message": "..." } }This is the local gateway envelope. The hosted relay uses a richer envelope with retryable and request_id — see relay errors. The two code sets are also different.
HTTP error codes
| Status | Code | Where |
|---|---|---|
| 401 | local_auth_required | any authenticated route without a valid bearer (response carries WWW-Authenticate: Bearer) |
| 400 | idempotency_key_required | POST /v1/sessions without the header |
| 400 | invalid_request | body too large (64 KiB), unknown fields, invalid combinations |
| 400 | unsupported_protocol | WebSocket upgrade without the speko.voice.v0.r3 subprotocol |
| 404 | session_not_found | stream attach or delete for an unknown session |
| 409 | idempotency_key_conflict | key reused with a different body |
| 409 | duplicate_session | the control plane returned an already-active session ID |
| 409 | stream_already_attached | second WebSocket consumer |
| 429 | local_concurrency_exhausted | at SPEKO_MAX_SESSIONS |
| 500 | request_fingerprint_failed | internal idempotency hashing failure |
| 502 | session_open_failed | the provider session could not be opened |
| 502 | control_plane_unavailable | plan fetch transport failure (managed mode) |
| 503 | gateway_draining | the process is shutting down |
| 504 | control_plane_timeout | control-plane deadline exceeded (managed mode) |
| passthrough | control_plane_rejected | the control plane refused the plan; the original status is preserved |
On control_plane_rejected, correlate with Speko support using the X-Control-Plane-Request-ID response header. The control plane's own codes (for example credit_exhausted, provider_not_entitled) are documented in Control plane API.
In-stream errors
Failures after a session is established arrive as the error event on the WebSocket, with retryable, terminal, and source fields.