Troubleshooting: VOICE_GATEWAY_ERROR (502) at call-send
VOICE_GATEWAY_ERROR is the API’s refusal code when the voice gateway — the
Jambonz / SBC layer that turns your API call into a call on the wire — never
answered the request. The call never reached the wire: no ringing, and a dispatch that fails
here is never billed. The error arrives as HTTP 502 or 503 with
code: "VOICE_GATEWAY_ERROR" in the standard error envelope (see
Error codes), and none of it means your payload
was wrong — the request was accepted and the gateway underneath failed or
was unreachable. This page separates the transient shapes you can safely
retry from the trunk-config shape you cannot, on each of the three
operations that raise this code.
The three operation lanes
Read the HTTP method and path of the failed request first — it tells you which lane you are in before anything else. 1. Outbound dispatch —POST /api/v1/voice/calls. The API handed the
call to the voice gateway and the gateway answered with an error, or never
answered within the dispatch timeout. A reconnect into the gateway is
retried for you on the transient classes; VOICE_GATEWAY_ERROR is what
surfaces when every attempt still failed. The cold-start shape (503 with a
“temporarily unavailable” message right after a platform rollout) lives
here too.
2. Voicemail-greeting synthesis and upload —
POST /api/v1/voice/voicemail/greetings (and its resubmit attempts). A
502 here means either the text-to-speech provider refused or returned empty
audio, or the rendered audio could not be pushed to storage. Both are
raised before any greeting row is written — the error response is the
whole outcome, nothing partially landed.
3. Mid-call actions — park, flip, transfer, and conference mutations on
a live call. The call stood up fine, but the follow-up control command to
the gateway failed — the far-end action (park, flip) never happened while
the call itself is usually still up. Verify the action actually landed
before treating the call as re-arranged.
First check — gateway side or trunk side
A 502 from dispatch sits on top of one of two layers: the gateway being genuinely down or cold (platform side), or your BYOC trunk being unreachable behind it (config side). Work the two existing runbooks by lane, instead of re-probing blindly:- Gateway side — if several dispatches fail close together or the error clustered right after a regional move, read the regional handoff first: failover and failback. A first-call failure at the handoff window that succeeds on retry is a handoff-window signal, not a trunk problem.
- Trunk side — dispatch reads the SIP trunk’s cached registration verdict before every call. Check your trunk rows:
unregistered, degraded, or with a non-zero
registrationFailureCount makes the gateway-side dispatch fail on the
trunk leg — work
SIP trunk registration, health, and failover
for that layer. In lane 1 the dispatch falls back through your failover
chain or the platform default instead of refusing, so a trunk problem
shows up here as a 502 only when nothing in the chain could take the
call.
- Lane 2 (greetings) — the gateway here is the synthesis provider plus storage, not your trunk. If every greeting attempt 502s while calls dispatch fine, the trunk records will not explain it; capture the request-id and escalate (below).
Retry-safety
VOICE_GATEWAY_ERROR is the transient class: the platform caught a
5xx-or-transport failure and no deterministic verdict was reached. All
three lanes are safe to re-issue — the operation is idempotent on this
failure: no call leg was created, no greeting row was partially written,
and the mid-call action either landed or did not (the error is raised only
when it did not). What differs is only HOW you re-issue:
- Honor
retry_afterwhen the envelope carries it. A 429-triaged or backpressured gateway may returndetails.retry_after(seconds); wait that long before the next attempt — do not hammer a cooling lane. - Use spaced retries otherwise. Two retries at growing gaps beat immediate re-issues: a cold-start window (the 90–120 s after a platform rollout) and a handoff window both close on their own.
- A retry that succeeds on the second attempt is the expected transient signature — the gateway or trunk layer recovered, nothing to fix.
- A retry that fails deterministically every time is no longer a gateway error in disguise — the layer underneath is telling you the call cannot route at all, and you are in the trunk-side branch of the check above.
What this code is NOT
Two sibling voice codes fail at a completely different layer — read thecode, not just the 502/503 status, before you treat a dispatch refusal as
a gateway failure:
VOICE_DNO_BLOCKED(422) — the destination number sits on a Do-Not-Originate list, and the call was refused BEFORE the gateway was ever asked. No retry will fix it; the destination is deliberately unreachable. See suspect voice destination.VOICE_CALLER_ID_REJECTED— the trunk refused your FROM identity (the caller-id was not in the trunk’s allowed list nor an org-owned DID). Also deterministic and also pre-gateway: fix the caller-id or the trunk identity configuration, do not retry.
When to capture and escalate
Escalate once the transient window has honestly closed — after your retries span the cold-start window above, the trunk-health check reads clean, and the same code keeps returning on the same lane. Capture the bundle BEFORE the retry storm so support can pull the single failing attempt without a back-and-forth:request_id— from the error envelope’smeta.request_id. This is the one field that lets support pull the exact gateway attempt.call_sid— from the response or the call-log row, when dispatch got far enough to mint one. For mid-call actions this is the live call’s SID; for lane 1 failures it may be absent (no call ever started).- Timestamp in UTC — when the 502 landed, and how long the streak ran.
- The lane and operation — dispatch vs greeting vs mid-call action, and the endpoint you called.
503 pre-session refusals: VOICE_GATEWAY_DEGRADED and VOICE_GATEWAY_DRAINING
VOICE_GATEWAY_ERROR fires when a call operation reached the gateway and
the gateway failed. VOICE_GATEWAY_DEGRADED and VOICE_GATEWAY_DRAINING
are the opposite shape: the voice gateway refuses the request BEFORE any
call leg, room, or media stream is created, because the media node that
received the request cannot take media work right now. Nothing was placed,
nothing was billed, and your configuration played no part in the refusal.
Where you meet them
POST /api/v1/voice-agent/session— 503VOICE_GATEWAY_DEGRADEDwith the message “Orbit Media native module unavailable” when the node’s media module failed to load. No call leg exists yet.POST /api/v1/voice-agent/realtime-session— 503VOICE_GATEWAY_DEGRADEDwith “LiveKit native module unavailable”, the same refusal on the realtime session path.- Media-fork starts on a live video room
(
POST /api/v1/video/rooms/:id/media-fork) — 503VOICE_GATEWAY_DRAINING(“This media node is draining. Retry.”) while the node is mid rolling-restart, or 503VOICE_GATEWAY_DEGRADED(“Media forks are unavailable on this node.”) when the media module failed to load on it. The gateway refusal is passed through the API unchanged. - While a node cannot take media work, the platform’s own health probe
reads
orbit_media: "degraded"from/health— the same condition you see as a 503, seen from the platform side.
What each code means
VOICE_GATEWAY_DEGRADED(503) — the media RTC native addon failed to load on the node that received the request. The node detects this at startup, reports it on/health, and refuses new media sessions instead of accepting them and producing dead air. It is a platform-side fault.VOICE_GATEWAY_DRAINING(503) — the node is mid rolling-restart: it finishes the work already in flight and refuses new work until it is back. It is the expected, brief state of a node during a platform rollout.
Retry guidance
- Retry with a short backoff (a few seconds, growing per attempt). A retry that lands on a healthy node succeeds with no other change on your side — that is the expected signature of both codes.
- A refused session create left nothing behind — no leg, no room, no billed second — so re-issuing the same request is safe.
- Never treat these as a tenant configuration fault. API keys, BYOK
credentials, and trunk settings play no part in a pre-session 503, and
re-checking them will not clear it. The config-side voice refusals are
the 502
VOICE_GATEWAY_ERRORtrunk lane (above) and the credential rejections —401/403STT_CREDENTIAL_REJECTEDand422 VOICE_CREDENTIAL_PROVIDER_REJECTED— which are deterministic and clear only on the config side.
When to escalate
A draining window lasts a node restart, and the same/health signal
feeds the platform’s readiness probing, so a degraded node is recycled
without your involvement; neither code should follow your integration for
long. If 503s on these codes persist for more than 15 minutes across
backoff-spaced retries, check the status
page, then open a ticket carrying the
meta.request_id from each refusal so support can pull the exact node and
attempt.
See also
- Error codes — the code table this page is the runbook for, and the retry-class matrix on it.
- 503 pre-session
refusals —
the section above:
VOICE_GATEWAY_DEGRADEDandVOICE_GATEWAY_DRAININGbefore any call leg exists. - Failover and failback — the first-call-after-handoff window, and how to read the trunk record to decide gateway-side vs trunk-side.
- SIP trunk registration, health, and failover — the trunk-layer branch: cached verdicts, probe results, and the fallback tags dispatch stamps onto the trunk detail page.
- Voice call quality — for calls the gateway DID place: one-way audio, dead air, and jitter are a different layer once the dispatch succeeded.