On WebSockets the same object arrives inside an error frame:
{"type": "error", "data": {"code", "message", "detail", "retryable"}}. On /ws/agent and /ws/stt, some runtime errors carry a plain string in data instead ({"type": "error", "data": "AI response service error."}); handle both.
Two responses do not use this shape: the synthesis capacity 503 (below) and the Call Center API’s billing errors (below).
Authentication Errors
Errors that indicate the client cannot authenticate. Do not retry these without user intervention.
SDK handling:
onError to the client’s constructor. It is a constructor option, not a property to assign later.
Validation Errors
Errors that indicate the request was malformed. Fix the request payload and retry.TTS Errors
Synthesis at capacity
When every synthesis slot is busy,POST /tts/synthesize answers 503 with a Retry-After header (seconds) and a body that is not the error-catalog shape:
Retry-After seconds and send the same request again.
Agent Errors
Billing Errors
Usage is paid from a prepaid balance (Pricing and billing). These errors mean the account cannot pay for what was asked; none of them goes away by retrying.
Where they arrive:
- REST (
/tts/synthesize,/stt/transcribe,/denoise): in the error format above. - Call Center API: as
{"code": "billing.topup_required", "error": "…"}, with the same codes. - WebSocket (
/ws/agent,/ws/tts,/ws/stt): the refusal comes before the connection opens. The handshake answers402with the message as plain text; a browserWebSocketonly sees the connection fail (close code1006), so check the balance when a connection fails at the handshake.
1008 and the reason account balance exhausted. See When the balance runs out.
Internal Errors
Reserved codes
These codes are defined, and appear in the SDKs’ErrorCode type, but the server does not send them today. Handle them generically (by retryable) if you match on codes exhaustively.
The JavaScript SDK’s
TTSClient uses internal.timeout for its own client-side timeout (no audio within 30 s, or 15 s without a chunk); that one is never sent by the server.
Client-side codes (JavaScript SDK)
These are raised by@lokutor/sdk itself, not sent by the server.
WebSocket Close Codes
When the connection closes, the WebSocket close code provides additional context:Retry Strategy
Decide fromretryable, and treat 402 (billing.*) as a prompt to top up rather than an error to retry. A complete recipe is in Error handling.