Skip to content

gemini-3.1-flash-live-preview: a 1.0 s mid-sentence pause closes the turn while the caller is still speaking (+ empty generation when thinking_level is set) #1328

Description

@IngLP

Description of the bug

With server-side (automatic) activity detection, a 1.0 s pause in the middle of one caller
sentence
makes gemini-3.1-flash-live-preview mis-segment the turn, reproducibly, in a
minimal configuration: a one-line system instruction (158 characters), no tools, no session
resumption, and no automaticActivityDetection block at all — the server's own default VAD is
what segments the speech. Every measurement below has a control arm in the same session that
differs only by the 1.0 s of silence.

  1. The turn is closed while the caller is still speaking. The input transcription of the
    first burst is delivered during the second burst — 24 TRIGGER trials out of 24, across
    four configurations, 0 out of 24 in the control arm of the same sessions. The model starts
    generating an answer to half a sentence, and in 21 of those 24 trials a turnComplete for
    that generation arrives 0.13–0.50 s before the last audio chunk of the utterance is sent;
    never in any control trial.
  2. The input transcription of the final burst is always slower than every control trial of the
    same session
    24/24. 1.61–1.78 s against 1.14–1.39 s in the default configuration.
  3. With thinkingConfig.thinkingLevel set, the generation on that boundary turn completes
    with zero output tokens
    usageMetadata with responseTokenCount absent (out_tokens=None
    in the SDK). 12 of the 15 trials that requested a thinking level, 0 of the 9 that did not.
    The caller gets no answer at all for that turn.

The same script, the same synthesised audio bytes and the same default configuration on
gemini-2.5-flash-native-audio-latest produce 0 empty generations out of 9 TRIGGER trials.
The 1.0 s pause costs about the same there — TRIGGER 0.56–0.64 s against CONTROL 0.03–0.17 s —
but it is added to a baseline an order of magnitude lower than 3.1's 1.14–1.39 s, and it never
empties a generation. The client is not what differs.

What this report is not. It is not a request to raise silenceDurationMs: we are aware the
value is capped at 2000 ms, and that committing a turn after a short silence has already been
described as expected behaviour
(forum 172306). This report is
about what happens after that commit: a turn closed while input audio is still arriving on the
wire, and — with a thinking level set — a generation billed with zero output tokens while the
caller is waiting.

Actual vs expected behavior

expected observed on gemini-3.1-flash-live-preview
turn boundary a turn is not committed, and no answer generated, while input audio for it is still being received first burst transcribed during the second burst: 24/24 TRIGGER, 0/24 CONTROL. turnComplete 0.13–0.50 s before the last audio chunk: 21/24 TRIGGER, 0/24 CONTROL
transcription of the final burst comparable to an uninterrupted utterance (1.14–1.39 s here) 1.61–1.78 s, 24/24 slower than every control trial of the same session
boundary turn output a completed generation produces output, or no generation is completed usageMetadata with no responseTokenCount: 12/15 with thinkingLevel set, 0/9 without
control model, same bytes, same config gemini-2.5-flash-native-audio-latest: 0/9 empty generations, TRIGGER delay 0.24–0.64 s

Environment

Model gemini-3.1-flash-live-preview (control: gemini-2.5-flash-native-audio-latest)
Transport google-genai Python SDK 1.73.1, client.aio.live.connect()
Endpoint / backend Gemini API — generativelanguage.googleapis.com, API key auth. Not Vertex AI.
Python / platform CPython 3.12.13, macOS 26.5.1 arm64
Audio PCM s16le, 16 000 Hz, 1 channel, 16-bit; audio/pcm;rate=16000; 20 ms chunks (320 samples / 640 bytes) sent every 20 ms, continuously, silence included
Activity detection no automaticActivityDetection block is sent — server default
System instruction 158 characters, one line (the --system-chars 60000 runs are a documented cross-check, not the default)
Sessions (UTC) 11 Live sessions, started between 2026-08-18T10:39:54Z and 2026-08-18T11:16:11Z; the exact start of each is in the ENVIRONMENT block of that run in the gist output

setup payload actually sent, verbatim and complete — this is the whole configuration under
test, nothing is elided:

{
  "setup": {
    "model": "models/gemini-3.1-flash-live-preview",
    "response_modalities": ["AUDIO"],
    "thinking_config": {"include_thoughts": false, "thinking_level": "MEDIUM"},
    "system_instruction": {
      "parts": [{"text": "Sei l'assistente telefonico di un servizio di consulenza. Parli solo italiano, con frasi brevi e cortesi. Rispondi sempre a voce a quello che dice il cliente."}],
      "role": "user"
    },
    "input_audio_transcription": {},
    "output_audio_transcription": {},
    "realtime_input_config": {
      "activity_handling": "START_OF_ACTIVITY_INTERRUPTS",
      "turn_coverage": "TURN_INCLUDES_ONLY_ACTIVITY"
    }
  }
}

Per-trial UTC timestamps for server-side log lookup are in the gist output. Happy to provide
our Google Cloud project number privately through whichever intake form you prefer — it is
deliberately not in this public body.

Reproducer

gemini_live_repro.py is in this gist: https://gist.github.com/IngLP/124873f0574ada091ab4324e5ff12432 — together with gemini_live_repro_output.txt, the unedited output of all eleven runs cited below. It is self-contained: no audio file is
shipped, the two speech pieces are synthesised at run time with the system TTS (say on macOS,
espeak-ng elsewhere), resampled to 16 kHz mono 16-bit PCM and level-matched, so the speech
bytes are byte-identical between the two arms
. The only difference between arms is 1.0 s of
PCM silence in the middle:

CONTROL   [head][tail]                        1.50 s, spoken back to back
TRIGGER   [head][1.0 s of PCM silence][tail]  2.50 s

Both arms end with the same final burst, so the delay measured from the last audio chunk of that
burst is attributable to the pause and to nothing else. Arms alternate inside one session, same
config, same connection.

export GEMINI_API_KEY=...
pip install google-genai==1.73.1

mkdir repro && cd repro          # put the script in a directory of its own; Python
                                 # puts the script's directory first on sys.path, so a
                                 # neighbouring types.py/enum.py shadows the stdlib

python gemini_live_repro.py --reps 3 --max-sessions 1                       # RUNS 1, 2, 11
python gemini_live_repro.py --thinking omit --reps 3 --max-sessions 1       # RUNS 3-4
python gemini_live_repro.py --system-chars 60000 --reps 3 --max-sessions 1  # RUNS 6-7
python gemini_live_repro.py --system-chars 60000 --thinking omit \
                            --reps 3 --max-sessions 1                       # RUN 5
python gemini_live_repro.py --model gemini-2.5-flash-native-audio-latest \
                            --reps 3 --max-sessions 1                       # RUNS 8-9
python gemini_live_repro.py --model gemini-2.5-flash-native-audio-latest \
                            --system-chars 60000 --reps 3 --max-sessions 1  # RUN 10
python gemini_live_repro.py --dry   # offline: synthesises the audio and audits the send cadence, spends nothing

One run is one Live session of ~50 s and 6 trials. --reps and --max-sessions only bound the
spend; no run in this report changes the configuration except where the flag says so, and
RUN 11 in the attached output is the script exactly as published, with no configuration flags at
all.

The audio stream is continuous — including during the pause

This is the first thing that gets asked (e.g.
dotnet-genai#269), so the script measures
it instead of asserting it. The "pause" is not a pause in sending: the caller leg pushes one
20 ms PCM frame every 20 ms for the whole session, and during the pause those frames are digital
zeros. Every trial prints chunks sent vs expected and the mean/max interval between consecutive
sends, for the whole utterance and separately for the pause window. From RUN 11:

TRIGGER #1  stream: 125 chunks sent during the utterance (expected ~125),
            interval mean 20.0 ms / max 24.1 ms;
            DURING THE 1.0 s PAUSE: 50 silence chunks (expected ~50), max interval 24.1 ms
[session1]  stream continuity over the whole session: 2519 chunks in 50.6s (expected ~2529),
            interval mean 20.0 ms / max 24.1 ms — the sender never stopped.

Worst inter-chunk interval anywhere in the eleven sessions in the gist: 25.0 ms (RUN 4), against
a 20 ms nominal period; the other ten stay under 24.2 ms. There is no gap in the stream for the
server to interpret as end of input.

Verbatim output

Full output of all eleven runs is in the gist as gemini_live_repro_output.txt (93 KB, unedited): https://gist.github.com/IngLP/124873f0574ada091ab4324e5ff12432.
The load-bearing part of one TRIGGER trial from RUN 11 — the script as published, no flags
except --reps 3 --max-sessions 1, which only bound the spend (t= is seconds from connect,
(+…) is seconds relative to the last audio chunk of the final burst):

  --- TRIGGER #1 -------------------------------------------------
    11:16:25.362Z  t= 14.16s  (+ -2.49s)  caller speech starts
    11:16:27.853Z  t= 16.65s  (+ -0.00s)  [!] 1 transcription fragment(s) arrived DURING the caller's speech: 'Buongiorno.'
    11:16:27.854Z  t= 16.65s  (+  0.00s)  last audio chunk of the FINAL burst sent
    11:16:27.415Z  t= 16.21s  (+ -0.44s)  server: interrupted
    11:16:27.492Z  t= 16.29s  (+ -0.36s)  server: turn_complete
    11:16:27.492Z  t= 16.29s  (+ -0.36s)  server: usage out_tokens=None
    11:16:29.466Z  t= 18.26s  (+  1.61s)  FIRST TRANSCRIPTION OF THE FINAL BURST: 'Sì, grazie.'
    11:16:30.256Z  t= 19.05s  (+  2.40s)  model audio starts
    stream: 125 chunks sent during the utterance (expected ~125), interval mean 20.0 ms / max 24.1 ms;
            DURING THE 1.0 s PAUSE: 50 silence chunks (expected ~50), max interval 24.1 ms
    => delay 1.61s, 1 EMPTY generation(s) (out_tokens=None)

Read the ordering: the server has committed the turn on the first burst and started answering it,
so the transcription 'Buongiorno.' is delivered at t=16.65 sinside the second burst — the
half-sentence answer is interrupted by the rest of the same sentence at t=16.21 s, and its
turnComplete at t=16.29 s, still 0.36 s before the caller's last audio chunk, carries
usage out_tokens=None.

The identical trial with no thinkingConfig at all (RUN 4) shows the same broken segmentation
and a non-empty interrupted generation:

  --- TRIGGER #2 -------------------------------------------------
    11:01:24.067Z  t= 27.18s  (+ -2.49s)  caller speech starts
    11:01:26.553Z  t= 29.67s  (+ -0.00s)  [!] 1 transcription fragment(s) arrived DURING the caller's speech: 'Buongiorno.'
    11:01:26.554Z  t= 29.67s  (+  0.00s)  last audio chunk of the FINAL burst sent
    11:01:26.130Z  t= 29.25s  (+ -0.42s)  server: interrupted
    11:01:26.166Z  t= 29.28s  (+ -0.39s)  server: turn_complete
    11:01:26.166Z  t= 29.28s  (+ -0.39s)  server: usage out_tokens=43
    11:01:28.260Z  t= 31.38s  (+  1.71s)  FIRST TRANSCRIPTION OF THE FINAL BURST: 'Sì, grazie.'
    11:01:28.265Z  t= 31.38s  (+  1.71s)  model audio starts
    => delay 1.71s

Summary tables (as printed by the script)

RUNS 1, 2 and 11 — gemini-3.1-flash-live-preview, default configuration (one-line system
instruction, thinkingLevel: MEDIUM; RUN 11 is the script with no configuration flags at all,
only the two that bound the spend):

arm         n    min    p50    max  no transcript  empty generations  turn cut mid-speech
CONTROL     3   1.14   1.25   1.27       0/3              0/3                  0/3        RUN 1
TRIGGER     3   1.61   1.61   1.61       0/3              3/3                  3/3        RUN 1

CONTROL     3   1.21   1.31   1.35       0/3              0/3                  0/3        RUN 2
TRIGGER     3   1.67   1.67   1.78       0/3              3/3                  3/3        RUN 2

CONTROL     3   1.25   1.38   1.39       0/3              0/3                  0/3        RUN 11
TRIGGER     3   1.61   1.72   1.77       0/3              2/3                  3/3        RUN 11

RUN 8 and RUN 9 — gemini-2.5-flash-native-audio-latest, same script, same audio bytes, same
default configuration:

arm         n    min    p50    max  no transcript  empty generations  turn cut mid-speech
CONTROL     3   0.12   0.14   0.15       0/3              0/3                  2/3        RUN 8
TRIGGER     3   0.56   0.57   0.62       0/3              0/3                  3/3        RUN 8

CONTROL     3   0.03   0.15   0.17       0/3              0/3                  2/3        RUN 9
TRIGGER     3   0.57   0.58   0.64       0/3              0/3                  3/3        RUN 9

Read honestly, the 2.5 "turn cut mid-speech" column is 4/6 in the control arm too. Those four
are not closed turns: they are the incremental fragment 'Bu' of a transcription still being
streamed, while every TRIGGER case is the complete 'Buongiorno.' of a committed turn. On 2.5 the
pause costs ~0.45 s and never an empty generation; on 3.1 the same column is 0/9 control against
9/9 trigger in the same default configuration, and 0/24 against 24/24 over all configurations.

It is not the size of the system instruction — it is the thinking level

The obvious objection to any report like this is "it is your configuration, not the model". So the
same script was run over the 2×2 of system-instruction size and thinking level, one to three Live
sessions per cell, 3 TRIGGER trials per session:

system instruction thinkingConfig TRIGGER trials turn cut mid-speech empty generations TRIGGER delay runs
158 chars (default) thinkingLevel: MEDIUM 9 9/9 8/9 1.61–1.78 s 1, 2, 11
60 000 chars thinkingLevel: MEDIUM 6 6/6 4/6 1.50–1.70 s 6, 7
158 chars not sent 6 6/6 0/6 1.61–2.08 s 3, 4
60 000 chars not sent 3 3/3 0/3 1.54–1.71 s 5

The broken segmentation and the delay are the same in all four cells — the size of the system
instruction changes nothing, and the delay is if anything largest in the leanest cell. Only effect
3, the zero-token generation, tracks the thinking level: 12/15 when a thinkingLevel is requested,
0/9 when the field is absent. In every control arm of all four cells the turn is never cut: 0/24.

One precision, because the API has no "thinking off": not sent means no thinkingConfig field in
the setup message, so whatever the server's default is applies. The claim is therefore "an
explicit thinkingLevel is what accompanies the empty generation", not "thinking causes it".

Scope and limits of the claim

  • What reproduces every time, in every configuration tested, is the mechanism: the turn is
    closed while the caller is still speaking (24/24 TRIGGER trials, 0/24 CONTROL), and the
    transcription of the final burst is slower than every control trial of the same session (24/24).
  • The empty generation is 12/15 with an explicit thinkingLevel and 0/9 without. It is
    reproducible in the minimal configuration — RUNS 1, 2 and 11 are 8/9 — but it is not
    configuration-independent, and this report does not claim it is.
  • What does not reproduce at a fixed size is the magnitude of the delay. An earlier in-house
    campaign with the same trigger (23 trials, two bursts separated by 1.0 s, same audio path)
    measured the final-burst transcription at 2.46–7.11 s with a median of 3.45 s, and 11 of
    those 23 above 5.8 s; in that campaign an empty generation accompanied 20 of the 27 turns
    whose delay was ≥ 2 s and 0 of the 45 below 2 s. In the runs attached here the same trigger
    produced 1.50–2.08 s. We are not claiming the large numbers are the norm — only that the
    mechanism is deterministic and the delay varies with load.
  • All measurements are clean 16 kHz PCM from a laptop straight to the API, not 8 kHz telephony.
  • 3 trials per arm per session is a small n per cell. The per-trial separation is what makes it
    readable: every one of the 24 TRIGGER trials is slower than every CONTROL trial of the same
    session, and the trials are interleaved inside one connection, so arm and moment cannot be
    confounded.

Related issues

Same underlying VAD segmentation, different symptom — none of them reports the empty generation
or the delay of the final burst's transcription:

  • python-genai#2580silence_duration_ms not honoured on 3.1 (reproduced by triage 2026-06-11)
  • js-genai#1467 — same, with a public repro that splits [speech][2.0 s silence][speech] into two turns
  • cookbook#1263 §2–§3 — effective 2000 ms cap, VAD cutting users mid-sentence
  • cookbook#1262 — VAD turn thrashing on 3.1, absent on 2.5 native audio
  • forum 172306 — official answer describing the early commit as expected and the 2000 ms cap

We are filing two other reports on the same model. They may share a root cause with this one,
but we have not demonstrated that: cookbook#1329, and a comment with a voice_activity
repro on python-genai#1285.

Questions

  1. Is a turnComplete emitted while input audio for that turn is still being received on the
    socket the intended behaviour of the default VAD?
  2. Is a completed generation with no responseTokenCount an expected outcome for a boundary turn
    that gets interrupted, and is there a client-side signal that distinguishes it from a
    generation that is still coming? Why does it appear only when thinkingLevel is set?
  3. Is there a supported way to keep the pre-3.1 segmentation behaviour on gemini-3.1-flash-live-preview
    short of disabling automatic activity detection entirely?

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions