Gemini-3.8-live: Live sessions are closed at exactly 58:00 with 1011 "Deadline expired"; resumption afterwards fails with 1007

Product: Gemini Developer API (API key, paid tier), Live API over WebSocket, v1beta
Model: gemini-3.8-live
Client: Python, pipecat-ai 1.9.0 on google-genai
Session config: response_modalities AUDIO; context_window_compression with sliding_window and trigger_tokens=7000; session_resumption enabled; history_config.initial_history_in_client_content=true; input and output audio transcription; 7 function declarations; model-owned turn taking.

What happens

We run a voice app with long continuous audio sessions. Every session that has reached one hour is closed by the server at exactly 58:00 after the WebSocket connected, with:

1011 (internal error) Deadline expired before operation could complete.

Date (UTC) Connected Closed with 1011 “Deadline expired” Elapsed
2026-09-29 12:21:01.905 13:19:01.912 58:00.007
2026-09-30 00:30:03.387 01:28:03.398 58:00.011
2026-10-01 14:09:34.965 15:07:34.963 57:59.998

In each case the same single connection stayed open for the whole period; there were no earlier closes or reconnects. Our client sets no timeout or deadline (only the x-goog-api-client header). Context window compression is on, so we expected the session to be unlimited, as documented.

What happens after the close

We reconnect with the latest session resumption handle:

  • 2026-09-29: the resumed session worked.
  • 2026-09-30: the resume was rejected within 0.35 s with 1007 (invalid frame payload data) Request contains an invalid argument.; the next handle got 1011 Internal error encountered.; a fresh session (history re-sent via client content) then worked.
  • 2026-10-01: the resume was accepted, but the first response took 22 s and the connection was closed 75 s later with the same 1007. Each following handle got 1011 Internal error encountered. Every fresh session we then opened (history re-sent via client content) took 16 to 22 s to start its first response and was closed with 1007 within 20 to 45 s, six times in a row.

We could not reproduce the 1007 with synthetic history (tool-call turns, up to about 75k tokens, compression on and off, live audio), so we cannot tell which argument is rejected.

Minimal reproduction

A standalone reproduction is running now: a single google-genai connection to gemini-3.8-live (compression with trigger_tokens=7000, session resumption on, no tools), sending 16 kHz PCM silence plus a short spoken clip every 60 s, logging any go_away and the close. I will post the result and the script as a reply when it finishes.

Questions

  1. Is 58 minutes a hard lifetime for gemini-3.8-live Live sessions, even with context window compression and session resumption? If so, can it be documented (and is a go_away sent before it)?
  2. Should a session resumption handle be valid after this close? Resuming gives either 1007 immediately or a session that dies with 1007 shortly after.
  3. On 2026-10-01, which argument triggered “Request contains an invalid argument” on fresh sessions seeded with prior history? Could the 1007 close reason name the offending field?

Reproduced with a standalone script (google-genai only, no pipecat, no tools, nothing from our app):

Event Time (UTC, 2026-10-02) Elapsed
Connected 09:02:52.413 0:00
54 normal turns (speech clip every 60 s, silence in between) 09:03 to 10:00
go_away, time_left=50s 10:00:02.164 57:09.8
Closed: 1011 (internal error) Deadline expired before operation could complete. 10:00:52.220 57:59.8

So the 58-minute lifetime is enforced server-side, with a 50 s go_away warning, and is independent of our client. Context window compression was on (trigger_tokens=7000). Only one session_resumption_update with a new handle arrived during the whole session.

Script (needs GEMINI_API_KEY and a few short 16 kHz mono PCM16 speech clips):

import asyncio, glob, os, time, wave, datetime
from google import genai
from google.genai import types

client = genai.Client(api_key=os.environ["GEMINI_API_KEY"], http_options={"api_version": "v1beta"})
CLIPS = [wave.open(p).readframes(10**9) for p in sorted(glob.glob("clips/*.wav"))]  # 16 kHz mono PCM16
SILENCE = b"\x00\x00" * 1600  # 100 ms
def ts(): return datetime.datetime.now(datetime.timezone.utc).strftime("%H:%M:%S.%f")[:-3]

async def main():
    config = types.LiveConnectConfig(
        response_modalities=["AUDIO"],
        system_instruction="Answer in one short sentence.",
        session_resumption=types.SessionResumptionConfig(handle=None),
        context_window_compression=types.ContextWindowCompressionConfig(
            trigger_tokens=7000, sliding_window=types.SlidingWindow()),
    )
    t0 = time.time()
    try:
        async with client.aio.live.connect(model="gemini-3.8-live", config=config) as s:
            print(ts(), "connected")
            async def mic():
                i = 0
                while True:
                    for _ in range(600):  # 60 s of silence
                        await s.send_realtime_input(audio=types.Blob(data=SILENCE, mime_type="audio/pcm;rate=16000"))
                        await asyncio.sleep(0.1)
                    clip = CLIPS[i % len(CLIPS)]; i += 1
                    for k in range(0, len(clip), 3200):
                        await s.send_realtime_input(audio=types.Blob(data=clip[k:k + 3200], mime_type="audio/pcm;rate=16000"))
                        await asyncio.sleep(0.1)
            async def rx():
                while True:
                    async for m in s.receive():
                        if m.go_away:
                            print(ts(), f"go_away time_left={m.go_away.time_left}")
            await asyncio.gather(mic(), rx())
    except Exception as e:
        print(ts(), f"closed after {(time.time() - t0) / 60:.2f} min: {e}")

asyncio.run(main())