in_progress — so this is always a
media-plane problem, not a connection problem. If the call never answers
at all, see
Call does not connect.
Work from the symptom. The dead direction tells you which half of the media
path to inspect first.
Which way is dead?
| Symptom | Dead leg | Look here first |
|---|---|---|
| The caller hears the agent; you hear silence | Uplink (caller → you) | NAT / RTP source address — the far end cannot reach the address you advertised. |
| You hear the caller; the caller hears silence | Downlink (you → caller) | A dropped AudioBridge handle, or an SDP a=sendonly / early-media direction. |
| Both connect, but one leg is noise, not silence | Either | A codec / payload-type mismatch on the negotiated leg. |
call_sid: voice/<sid>/uplink (caller audio) and
voice/<sid>/downlink (audio back to the caller). They fail independently.
That is why one direction can die while the other continues to work. See
Sessions, calls, tracks & streams
for the track model.
Cause 1 — NAT / RTP path (the classic one-way)
This is the most common cause. The SDP you answered with advertises the address the far end must send RTP to. Behind cloud NAT, the engine binds on a private address but must advertise its public one. If the public RTP address is missing or wrong, the carrier streams caller audio into a black hole. Your egress still reaches the address they advertised. So the caller hears you, and you hear nothing.| Cause | Fix |
|---|---|
The trunk advertises a private RTP IP in the SDP c= line. The private bind leaked into the answer. | Set the trunk’s external RTP IP to the engine’s public address. The SDP then carries a reachable c=. The private bind stays internal. |
| Asymmetric RTP: the far end sends media from a different port than it signalled, so latching never occurs. | The media plane latches egress to the source of the first inbound RTP packet. If no inbound packet arrives (see above), there is nothing to latch to. Fix the advertised address first. |
| A firewall or SBC in front of the trunk drops inbound RTP. | Open the RTP port range to the carrier’s media IPs. Check that the SBC rewrites the SDP to its own public media address. |
Cause 2 — SDP direction attribute
An SDP media line carries a direction:sendrecv (both ways), sendonly,
recvonly, or inactive. If early media (the 183 Session Progress) or the
final answer negotiated anything other than sendrecv, the negotiation
intentionally muted one direction.
| Cause | Fix |
|---|---|
The far end offered a=sendonly (announcement / hold-style early media) and never renegotiated to sendrecv. | This is expected during ringback. If it continues after the answer, the caller PBX holds the leg. Check for a stray hold/re-INVITE in the trace. |
The answer went out on the 183 with early media, but the 200 OK SDP differs. | The answer sequence must be 100 → 183 with SDP → 200 with the same SDP. A mismatched or SDP-less 200 desyncs the media direction. It can also trigger a carrier BYE. |
The call is on hold — a re-INVITE flipped the leg to inactive/sendonly. | Resume the leg. Look for a reinvited event in the call lifecycle. |
Cause 3 — Codec / payload-type mismatch
When one leg is dead as silence, the cause is usually a path problem (Cause 1/2). When one leg is dead as noise or static, suspect the codec. The far end streams a payload type that you did not answer with. Frames arrive, but they decode to garbage. The bridge transcodes automatically across everything it negotiated: µ-law (PCMU, PT 0), A-law (PCMA, PT 8), and Opus. This includes µ-law ⇄ A-law between two mismatched G.711 legs. One-way noise means the far end sends a format that was never in the answer. There is nothing to transcode it to.| Cause | Fix |
|---|---|
| The carrier streams a payload type that is absent from your SDP answer. | Offer both PCMU and PCMA, so any PSTN carrier can pick one. Let the bridge transcode to your chosen AudioCodec. |
| A static PT number is reused for a different codec (a non-standard carrier). | Pin the codec on the trunk. Check that the negotiated rtpmap in the trace matches what the carrier sends. |
Cause 4 — the dropped AudioBridge handle
If the caller stops hearing you mid-call, but the SIP dialog is still up and
your onUplink callback still fires, you almost certainly dropped the
downlink publisher. Keep the bridge for the whole lifetime of the call.
Close it explicitly on hangup.
- TypeScript
- Python
In the synchronous cores (Rust, Go), the bridge closes on drop (RAII).
When the value falls out of scope, both tracks tear down. Bind the bridge to a
variable that lives as long as the call — for example,
let bridge = voice.audio_bridge().attach(...)?; held in your session struct.
Do not use a temporary.Diagnose it in order
1
Confirm the dialog is actually up
Check that the call reached
in_progress and is still there. If the call
dropped, this is a connection problem, not one-way audio. Go to
Call does not connect.2
Establish which direction is dead
If only the caller hears you ⇒ the uplink is dead ⇒ start at Cause 1
(NAT/RTP path). If the caller hears silence ⇒ the downlink is dead ⇒
start at Cause 4 (bridge handle), then Cause 2 (SDP direction).
3
Read the packet trace
In the SIP/RTP trace viewer, look for RTP flow on each leg separately.
No inbound RTP on a leg = the far end cannot reach your advertised address
(Cause 1). Inbound RTP present but the audio is static = a codec mismatch
(Cause 3). See
SIP/RTP debugging.
4
Inspect the negotiated SDP
Check that the
c= line carries a reachable address, and that the
media line is sendrecv. A private c= IP or a sendonly/inactive
direction is your answer.5
Audit your bridge lifetime
If the dead direction is the one your code publishes, check that you hold
the
AudioBridge handle for the whole call. Call close() only on
hangup.6
Check MOS if audio is present but poor
One-way audio is binary: present or absent. If audio flows both ways but
is choppy or clipped, that is a quality problem. See
MOS, jitter & loss.
Human-agent downlink (browser softphones). When you hand a call to a human
agent, the human hears the caller over a media-over-QUIC track in the browser
softphone. That return-audio (downlink) path is still being hardened — the
caller-audio origin publish is being bridged into the relay fan-out. If a
browser human agent reports one-way audio (the agent can talk but cannot
hear the caller), validate that leg in staging. Real SIP deskphone agents
are not affected. Their media rides the normal RTP bridge. See
AI ⇄ human handoff.
Related
SIP/RTP debugging
Read per-leg SIP and RTP traces to find the dead direction.
Codec guide
Pick a codec per leg; where transcode is free and where mismatches bite.
Call does not connect
When the dialog never reaches in_progress in the first place.
Sessions, calls, tracks & streams
Why uplink and downlink are separate tracks that fail independently.
SIP trunking
Trunk fields for internal vs external RTP IP behind cloud NAT.
AI ⇄ human handoff
The human-agent downlink caveat, and how the return-audio path is wired.

