This page is about the agent failing to yield. If the opposite is
happening — the agent cuts out on background noise or a cough — you want the
sensitivity tuning in
Turn detection & barge-in, not this
page.
Symptom → cause → fix
| What you hear | Likely cause | Fix |
|---|---|---|
| Caller speaks clearly over the agent, agent never stops | VAD gate too high — threshold set aggressively for a noisy trunk, so real speech reads as background | Lower threshold toward 0.5; confirm the caller’s speech actually crosses it (see debugging) |
| Agent ignores short interruptions (“wait—”, “no”) but yields to long ones | Over-agent floor too high — params.response_min_speech_ms raised past the length of a real interjection | Lower response_min_speech_ms (default 600) toward 300; it is deliberately higher than the answering floor to reject backchannels |
| Interrupt only lands after a long, obvious over-talk | Hold-and-confirm window too long — params.barge_confirm_ms set high | Lower barge_confirm_ms (default 300); set 0 only on echo-cancelled legs (browser) |
| Agent won’t yield on a phone call, but yields fine in the browser | TTS mic gate bleed — params.margin_while_tts raised so the caller is gated out while the agent speaks | Lower margin_while_tts on that agent, or move to a leg with client-side echo cancellation — see below |
| Playback goes quiet the instant the caller speaks, then the agent resumes and finishes its sentence | Provider can’t cancel mid-flight — non-streaming HTTP LLM. The barge is detected, but only the local playback is silenced | Move that node to a streaming or realtime provider — see the scorecard |
| Agent replies during the caller’s natural pause, sounds like it’s talking over them | End-of-turn timer too short — silence_duration_ms fires on the gaps between words | Raise silence_duration_ms (default 500) toward 800 for deliberate callers |
Isolate the cause
Run these in order. The goal is to answer one question at a time: did the VAD see the caller, was the caller gated out, and can the provider actually stop?Confirm the interrupt is even detected
Pull the turn-taking trace for the call (the runtime records every speech-onset,
pending-barge, confirm, and cancel event; the workspace surfaces them per call).
See Barge-in debugging.
- No onset logged while the caller is clearly speaking → the VAD gate is too high. Go to step 2.
- Onset logged, but no confirm → the caller spoke, but not long enough to
clear
barge_confirm_msorresponse_min_speech_ms. Go to step 3. - Confirm logged, but the agent kept talking → detection is fine; the provider couldn’t cancel. Go to step 4.
Lower the VAD gate (thresholds too high)
On a noisy trunk it’s tempting to raise Start at the
threshold to reject background chatter.
Push it too far and the caller’s real speech never crosses the gate, so no
onset is ever armed.0.5 default and only nudge upward in small steps while watching
that onsets still fire. If the line is genuinely noisy, the durable fix is a
cleaner trunk or the ingress noise suppression on the browser/WebRTC leg — not a
higher gate that also rejects the caller.Lower the confirm and over-agent floors (won't yield to short interrupts)
Two knobs decide whether a detected onset becomes a confirmed barge, and
both are set high on purpose to keep backchannels from cancelling the agent:
params.barge_confirm_ms(default300) — how long over-talk must sustain before the interrupt fires.params.response_min_speech_ms(default600) — the shortest burst that counts as a real interruption over the agent (vs. a continuer like “mhm”).
barge_confirm_ms: 0 for an instant cut.Confirm the provider can cancel mid-flight (timeout-only barge)
If the interrupt is confirmed but the agent finishes its sentence anyway, the
downstream provider is the limit — not your tuning. A non-streaming HTTP LLM has
no way to stop generating once the request is in flight, so the runtime can only
silence the local playback; the response still completes server-side (and is
still billed).Check the provider path against the
barge scorecard.
Streaming TTS, streaming ASR, and realtime speech-to-speech providers cancel
cleanly within a frame. Move the offending node onto one of those paths.
Check the end-of-turn timer if the agent replies into a pause
This one is subtler: the caller hasn’t interrupted at all — the agent thought
the turn was over. If
silence_duration_ms is shorter than the caller’s natural
between-word pauses, end-of-turn fires early and the agent starts replying while
the caller is still thinking. From the caller’s seat it’s indistinguishable from
“the agent won’t let me finish.”Raise silence_duration_ms (default 500) toward 700–800 for deliberate
callers before you touch anything else.The TTS mic gate and no-AEC legs
There is no acoustic echo cancellation in the telephony media path. On a phone call the agent’s own TTS leaks back through the caller’s microphone. Left alone, that leaked audio would trip the VAD and the agent would barge on itself. To stop that, the runtime raises the during-speech mic gate —params.margin_while_tts
— while the agent is talking. On PSTN it goes all the way up to match the
quiet-line gate (params.margin), so the agent’s voice can never cancel the
agent. Realtime telephony sessions force this automatically.
The catch: that same gate is what the caller’s speech has to cross to barge in.
Set it too high and you don’t just reject the echo — you reject the caller.
| Leg | Echo cancellation | margin_while_tts guidance |
|---|---|---|
| SIP / RTP (PSTN trunk) | None — no AEC in the media path | Keep the guard high enough to reject echo; lower cautiously and re-test that the agent doesn’t self-barge |
| Browser (media-over-QUIC) | Client-side (browser AEC) | Safe to lower for a snappier, more sensitive barge |
| WebRTC fallback leg | Client-side (browser AEC) | Same as browser |
When the provider owns the turn
If your agent’s entry node is a realtime speech-to-speech provider, the provider does its own endpointing (server_vad) — the on-device VAD never runs. In that
mode the barge signal is whatever the provider emits, and how fast it stops is
the provider’s budget, not yours.
- OpenAI Realtime, Gemini Live — emit clean speech-start / speech-stop events; the runtime forwards the interrupt/truncate and the session stops generating. This is the tightest path.
- Self-hosted models with no server VAD — there is nothing to detect the turn provider-side. The runtime falls back to a local detector driving an explicit commit, so barge-in still works, but you tune it with the same knobs above. See Bring your own speech-to-speech.
- External vendor bridge (LiveKit / Vapi / Twilio) — the interrupt is only as good as that vendor’s adapter. For the tightest budgets, keep media on the native SIP/RTP, browser, or WebRTC-fallback path instead.
Still stuck?
Reproduce with the defaults
Strip the agent’s
turn_detection block back to empty and reproduce. The
defaults are phone-safe; if barge-in works with no config, one of your overrides
is the cause — reintroduce them one at a time.Diff the browser and phone legs
If it only fails on one transport, the TTS mic gate (above) or the provider path
is the difference — not the caller.
Related
Turn detection & barge-in
The full model — VAD modes, the timing knobs, and the barge scorecard by
provider and transport.
Barge-in debugging
Trace onset, pending-barge, confirm, and cancel events on a live call.
Runtime configuration
Where
turn_detection lives in the agent config, and how to push it.High latency
If the agent yields but the whole turn feels slow, start here instead.