Errors & troubleshooting
One page for every failure mode across the text tools and the speech services — including the ones that do not produce an error, which are the expensive ones to debug.
HTTP status codes
Section titled “HTTP status codes”| Code | Where | What it means |
|---|---|---|
200 |
all | OK |
400 |
all | Malformed request, an unknown field, an unsupported language code, or an empty required field. On gpu-tts / gpu-voice also: input over 4,000 characters, an unknown or retired voice, a language that is not offered, "voice": "default" in a language with no ready-made voice (use design), design without instructions (or with instructions over 400 characters or input over 1,000), instructions on a ready-made voice, or ref_audio / ref_text (cloning is paused). On gpu-transcribe: unsupported_language for a language other than en es fr de pt ru |
401 |
all | Unknown or revoked GPU API key (unauthorized) |
403 |
all | Missing X-NH-GPU-Key — blocked at the edge, before it reaches any service; or not_approved / forbidden (key without the GPU scope) from the lane. Can also mean your HTTP client — see below |
404 |
all | Wrong path for that capability, or a paused voice studio route on gpu-voice (/speakers, /voice_design, /custom_voice, /clone). GET /v1/models exists only on ai.ollalink.com |
410 |
ai.ollalink.com |
gone — a route switched off on this host |
413 |
ai.ollalink.com, gpu-transcribe |
too_long / too_large — text, file or audio over the limit; split it |
429 |
all | Rate limited — 600 requests / 60 s per key. Back off and honour Retry-After |
503 |
speech hosts, ai.ollalink.com |
Busy or briefly unavailable — retry shortly with back-off (gpu-asrhq sends busy with Retry-After: 1; gpu-tts sends service_unavailable; translation sends translation_unavailable, embeddings embeddings_unavailable, classification classification_unavailable). On sound-stream.ollalink.com: the service is paused |
5xx |
all | Temporary upstream failure |
Error bodies are JSON with an error object:
{"error":{"code":"too_long","message":"`text` is 5400 characters; the limit is 5000 per request"}}On gpu-tts / gpu-voice the object carries message and type ({"error":{"message":"…","type":"invalid_request_error"}}).
A 403 that is not about your key
Section titled “A 403 that is not about your key”The edge rejects some automated-client signatures. Python’s standard library
urllib.request is blocked: its default User-Agent (Python-urllib/3.x) returns 403
with a valid key, which looks exactly like an auth failure.
requests, httpx, the OpenAI SDK, curl and browsers all work. If you must use urllib, set
your own User-Agent.
WebSocket failures
Section titled “WebSocket failures”Authentication
Section titled “Authentication”If the X-NH-GPU-Key header is missing, the WebSocket upgrade itself is rejected with HTTP 403. You never get a
connection, so you never get a JSON error frame — catch the handshake exception, not a message. A GPU API key that is
unknown, not approved or lacks the GPU scope connects, receives one error frame (unauthorized, not_approved,
forbidden) and is closed with code 1008.
# websockets raises InvalidStatus before the context manager body runsexcept websockets.exceptions.InvalidStatus as e: print(e.response.status_code) # 403Error events
Section titled “Error events”When an error is reported, it arrives as a JSON frame and the socket usually closes. Live captions send
{"type":"error","code":…,"detail":…}; the realtime lane sends {"type":"error","error":{"code":…,"message":…}}.
code |
Lane | Meaning |
|---|---|---|
busy |
live captions · realtime | The shared live speech capacity is full. Closed with 1013 at once — retry with back-off |
unavailable |
realtime | Recognition failed or is unavailable; closed with 1011 — open a new connection |
unsupported_recognition_source |
live captions | source_lang is kn, ta, te or bn; closed with 1008 |
unauthorized · not_approved · forbidden |
both | Key unknown, not approved, or without GPU access; closed with 1008 |
Failures that are silent
Section titled “Failures that are silent”These do not raise an error. Get them wrong and you get poor output or nothing at all, with no diagnostic — so check them first when a stream “connects but does nothing”:
| What you did | What happens |
|---|---|
Sent a sample_rate other than 16000 |
Accepted — you still receive ready, but the audio is misinterpreted and transcription quality collapses. Always resample to 16 kHz mono s16le first |
Sent audio before the config / session.update message |
Silently ignored — no error, no transcript |
Did not wait for session.updated before streaming (realtime speech-to-text) |
Early audio is dropped |
| Sent audio faster than real time | Backlogs and degrades; pace frames to wall-clock |
| Sent stereo or non-PCM audio | Misinterpreted, not rejected |
| Expected translated captions | Live captions are captions only: target_lang is ignored and no translation events arrive. Translate the finals with POST /v1/translate |
Convert anything before streaming:
ffmpeg -i input.mp3 -ar 16000 -ac 1 speech_16k_mono.wavQuick triage
Section titled “Quick triage”| Symptom | Look at |
|---|---|
401 |
Unknown or revoked key — check Authentication |
403 |
Missing key, key not approved or without GPU access, or your HTTP client’s User-Agent |
410 on ai.ollalink.com |
A route switched off on this host; the message names the routes that are served (Models) |
404 |
Wrong path — not every lane is OpenAI-shaped, only ai.ollalink.com serves /v1/models, and the voice studio routes are paused |
503 that clears on retry |
Busy or restarting; retry with back-off |
503 on sound-stream |
Paused — see Realtime speech |
| WebSocket refuses to open | Auth fails at the handshake as HTTP 403 |
| WebSocket closes with 1013 | The shared session budget is full; retry with back-off |
| WebSocket opens but stays silent | Wrong sample_rate, or audio sent before config |
| Transcript is garbage | Audio is not 16 kHz mono s16le |
