# Barge-in Debugging

> Trace why an interrupt did or did not fire on a live call — VAD knobs, the provider cancel scorecard, and no-AEC self-trigger.

When barge-in misbehaves, the cause is almost never random. There are three
usual causes. The runtime never detected the interruption. Or the runtime
detected it, but the downstream provider could not be cancelled mid-flight. Or
the agent's own voice looped back through a phone line with no echo
cancellation and triggered a phantom barge. This page is the diagnostic path:
reproduce the call, look at the one artifact that shows the truth, and map the
symptom to the knob or provider that caused it.

If you want only the config reference, read
[Turn detection & barge-in](/modalities/voice/concepts/turn-detection) first.
This page assumes that you know what `barge_confirm_ms` and `server_vad` are.
It starts where those settings do not behave.

## The stereo recording is your ground truth

Before you touch a knob, get the recording. The system captures every call as
a **stereo WAV: the caller on the left channel, the agent on the right**. This
makes barge-in failures visible at a glance. Open the two channels side by
side. You can see exactly where they overlap and for how long.

1. **Pull the recording for the call_sid**
Every call keys off its `call_sid`. Find the failing call in your reports
view. Open its recording from the call's detail page. See
[Call traces](/modalities/voice/observability/call-traces) to find a call by
`call_sid`, DID, or time window.
2. **Read the two channels against each other**
- The agent (right) continues while the caller (left) is loud → the interrupt
  never fired, or it fired but the provider could not cancel. Go to
  [the symptom table](#symptom-cause-fix) and the
  [provider scorecard](#provider-cancel-scorecard).
- The agent (right) cuts out exactly when its own audio is loudest, and there
  is no caller speech on the left → a self-trigger. Go to
  [no-AEC self-trigger](#no-aec-sip-legs-self-trigger).
- The agent cuts on a short "mhm" on the left → the runtime treated a
  backchannel as an interruption. Raise `response_min_speech_ms`.
3. **Confirm on a live call with monitor**
Reproduce the problem with a supervisor **monitor** session. You hear both
legs in real time while you change a knob. Monitor is listen-only and does not
change the call. See
[Supervise a call](/modalities/voice/observability/call-traces).

> **NOTE:**
> There is **no shipped per-turn barge-event telemetry stream or dashboard**
> today. You diagnose barge-in from the stereo recording, a live monitor
> session, and the knobs below — not from a "barge timeline" chart. A turn-level
> event feed is on the roadmap. Do not wait for it to debug a call.

## Symptom, cause, fix

| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| The agent talks straight over the caller and never yields | The provider cannot be cancelled mid-flight (an HTTP LLM, or a vendor bridge with no interrupt event) | Check the [scorecard](#provider-cancel-scorecard). Move to a streaming/realtime provider or fix the vendor adapter |
| The agent yields on a phone call but not in the browser (or the reverse) | The interrupt fires on one transport leg but the provider path differs | The VAD does not depend on the transport. The difference is the provider. Check that the same agent/provider is on both legs |
| The agent cuts itself off mid-word, with no caller speech present | Its own TTS leaks back through a no-AEC phone mic and self-triggers | Raise `margin_while_tts` to match `margin` on PSTN (the default on phone). See [below](#no-aec-sip-legs-self-trigger) |
| The agent replies mid-sentence and chops the caller | `silence_duration_ms` is too low. End-of-turn fires on inter-word pauses | Raise `silence_duration_ms` toward 500–800 ms |
| A short "mhm"/"ok"/"right" cancels the agent | `response_min_speech_ms` (the over-agent floor) is too low | Raise `response_min_speech_ms` (default 600). The backchannel then self-drops |
| The system ignores a genuine one-word answer ("yes", a digit) | `answer_min_speech_ms` is too high in the answering window | Lower `answer_min_speech_ms` (default 150) so that terse replies still commit |
| Barge feels laggy even on a good provider | The `barge_confirm_ms` hold window is the floor of interrupt latency | Lower `barge_confirm_ms`. Set `0` **only** on echo-cancelled transports |
| Barge silences the audio but the LLM still runs (and bills) | An HTTP / non-streaming LLM completes server-side in all cases | This is expected. Use a streaming or realtime model for a true mid-flight cancel |
| Clicks or line noise trigger false interrupts | `min_speech_ms` / `threshold` are too permissive | Raise `min_speech_ms`. Raise `threshold` on noisy trunks |

## The four knobs you'll actually reach for

These levers are behind almost every barge-in fix. They live under
`turn_detection` (and `turn_detection.params`) in the agent config. The full
schema is in [Runtime configuration](/modalities/voice/runtime/configuration).

| Knob | Governs | Turn it when |
|------|---------|--------------|
| `silence_duration_ms` | The trailing silence before **end-of-turn** fires (when the agent may reply) | The agent replies mid-sentence → raise it. The agent feels sluggish → lower it |
| `min_speech_ms` | The shortest burst that counts as speech at all (a noise/click filter) | Line noise causes false triggers → raise it |
| `barge_confirm_ms` | The **hold-and-confirm** window: how long over-talk must continue before the interrupt fires | Backchannels cancel the agent → raise it. The interrupt feels laggy → lower it (or set `0` on echo-cancelled legs) |
| `margin_while_tts` (the TTS guard) | How hard the mic gate clamps **while the agent speaks** | PSTN self-trigger → raise it to match `margin`. The browser wants a faster cut → lower it |

The `min_speech_ms` floor depends on the position in the turn. Over the agent,
the floor is `response_min_speech_ms` (higher, to reject backchannels). In the
answering window, the floor is `answer_min_speech_ms` (lower, so a one-word
reply still counts). Debug the two windows separately. "The agent won't stop
for me" is a different knob than "the agent ignored my quick yes."

> **WARNING:**
> Backchannel suppression is **acoustic only**. It keys off *how long* the
> caller speaks, not *what* they say. There is no lexical gating. "Stop" and
> "uh-huh" look the same to the turn detector. If you need word-aware
> interrupts, handle them in tool logic on your side.

## Provider cancel scorecard

The on-device VAD **always** detects the interruption. Whether the agent's
audio actually stops depends on the downstream provider. Some providers cannot
be cancelled mid-flight. A "timeout-only" provider is not a bug that you can
tune away. The runtime silences local playback immediately, but the provider
continues to generate (and bill) until it finishes on its own.

| Provider path | Mid-flight cancel | On barge |
|---------------|-------------------|----------|
| OpenAI Realtime (`server_vad` or local VAD) | Clean | The runtime sends the truncate/interrupt. The session stops generation |
| Gemini Live | Clean | The runtime sends the provider interrupt. Generation stops |
| Streaming TTS — ElevenLabs, Deepgram | Clean | The runtime flushes the outbound audio buffer within a frame. Playback stops at once |
| Streaming ASR — Deepgram | Clean | The cancel propagates. The runtime discards the partial transcript |
| HTTP / non-streaming LLM — OpenAI HTTP, Anthropic, Gemini text, Ollama | **Timeout-only** | The runtime silences playback locally. The request runs to completion server-side and is still billed |
| External vendor bridge — LiveKit, Vapi, Twilio | Depends on the vendor's own interrupt event | Only as clean as that vendor's adapter. See below |

> **WARNING:**
> Some calls route media through an external agent-orchestration vendor instead
> of the built-in pipeline. There, the barge signal is only as good as that
> vendor's adapter. Some adapters do not yet send their interrupt event on
> barge. Then the agent continues to talk even though the runtime detected the
> interruption. For the tightest interrupt budgets, keep media on the native
> SIP/RTP, browser, or WebRTC-fallback path.

**Fast triage:** the stereo recording shows the agent talking over the caller,
*and* the agent is an HTTP LLM or a vendor bridge? Then the VAD knobs will not
help. That is a provider-capability limit, not a tuning problem. Switch to a
streaming/realtime provider or fix the adapter.

## No-AEC SIP legs self-trigger

The most confusing barge-in bug: the agent interrupts *itself*, with no caller
speech anywhere on the recording. This failure mode occurs only on telephony.

There is **no acoustic echo cancellation in the media path.** On the browser
path that is fine, because the browser cancels echo before the audio reaches
us. On a PSTN/SIP leg there is no such cancellation. The agent's own TTS
travels down the line. It bounces off the far-end handset or hybrid. It comes
back up the caller's mic. To the VAD, that returning audio looks exactly like
the caller speaking over the agent. So the VAD fires a barge and cancels the
agent mid-word.

The runtime guards against this with the **TTS guard**. While the agent
speaks, the runtime raises the during-speech mic gate (`margin_while_tts`).
Thus quiet echo cannot cross the speech threshold. On phone calls, the runtime
clamps `margin_while_tts` up to match the quiet-line gate `margin`. Thus the
agent's own voice can never cancel the agent.

1. **Confirm it is a self-trigger, not a real barge**
On the stereo recording, the agent (right) drops out while it is loudest. The
caller (left) is silent or carries only a faint echo of the agent. That is a
self-trigger.
2. **Check the transport**
A self-trigger occurs only on no-AEC legs: SIP/RTP PSTN trunks. If the leg is
a browser (WebTransport) or WebRTC-terminated leg, echo is cancelled upstream.
Then this is not your cause. Look elsewhere.
3. **Raise the TTS guard on PSTN**
Check that `margin_while_tts` is clamped to `margin` on the phone leg (the
default). Perhaps you lowered it for a faster browser cut, and that same agent
also takes phone calls. Then the phone leg is under-guarded. Restore the clamp
for PSTN.
4. **Re-test with hold-and-confirm**
Keep `barge_confirm_ms` non-zero on PSTN. A brief echo tail ends before the
confirm window elapses. Thus even a leak that crosses the gate self-drops and
does not cancel the agent. Reserve `barge_confirm_ms: 0` for echo-cancelled
browser legs.

> **TIP:**
> Rule of thumb: on echo-cancelled transports (browser) you can be aggressive.
> Use a low `margin_while_tts` and `barge_confirm_ms: 0` for a razor-fast cut.
> On no-AEC transports (PSTN) stay conservative. Clamp the TTS guard and keep a
> non-zero confirm window. One agent that serves both should default to the
> conservative side.

## A decision path

1. **Does the recording show real caller speech over the agent?**
No, and the agent still cut → a **self-trigger**. Go to
[no-AEC self-trigger](#no-aec-sip-legs-self-trigger). Yes → continue.
2. **Did the agent stop when the caller spoke?**
No → the interrupt was not actioned. Check the
[provider scorecard](#provider-cancel-scorecard). An HTTP LLM or a vendor
bridge cannot be cancelled mid-flight, whatever your knobs are.
3. **Did it stop, but too eagerly or too late?**
Too eagerly on a short sound → raise `response_min_speech_ms` /
`min_speech_ms`. Too late → lower `barge_confirm_ms`. Replies chop the caller
mid-sentence → raise `silence_duration_ms`.
4. **Still stuck?**
The problem is likely not barge-in at all. A one-way-audio or high-latency
problem can look like "the agent won't respond." Cross-check
[Latency breakdown](/modalities/voice/observability/latency-breakdown) and the
[Barge-in not working](/modalities/voice/troubleshooting/barge-in)
troubleshooting guide.

## Related

- **[Turn detection & barge-in](/modalities/voice/concepts/turn-detection)** — The config reference for every knob on this page.
- **[Barge-in not working](/modalities/voice/troubleshooting/barge-in)** — Step-by-step fixes when the agent does not yield to the caller.
- **[Call traces](/modalities/voice/observability/call-traces)** — Find a call by `call_sid`, open its recording, and monitor it live.
- **[Latency breakdown](/modalities/voice/observability/latency-breakdown)** — Rule out latency that masquerades as a barge-in failure.
