Skip to main content

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:
A trunk at 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_after when the envelope carries it. A 429-triaged or backpressured gateway may return details.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 the code, 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.
Both of those are tenant-config problems masquerading as dispatch failures — neither involves the gateway answering with an error, and neither clears by waiting.

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:
  1. request_id — from the error envelope’s meta.request_id. This is the one field that lets support pull the exact gateway attempt.
  2. 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).
  3. Timestamp in UTC — when the 502 landed, and how long the streak ran.
  4. The lane and operation — dispatch vs greeting vs mid-call action, and the endpoint you called.
Do not keep re-issuing as a fix attempt beyond a few backoff-spaced retries on the same lane — a deterministic 502 stays deterministic, and each repeat only moves the same failure further into the log window.

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 — 503 VOICE_GATEWAY_DEGRADED with 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 — 503 VOICE_GATEWAY_DEGRADED with “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) — 503 VOICE_GATEWAY_DRAINING (“This media node is draining. Retry.”) while the node is mid rolling-restart, or 503 VOICE_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.
Both codes are transient and per-node: the next request is balanced across the fleet, and any healthy node accepts it.

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_ERROR trunk lane (above) and the credential rejections — 401/403 STT_CREDENTIAL_REJECTED and 422 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_DEGRADED and VOICE_GATEWAY_DRAINING before 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.