What happens
Every Live API session that exchanges a turn records exactly one 409 Conflict on the "API errors" chart of the AI Studio usage dashboard. A session that connects and closes without sending anything records none - that is the line between the two, and it is reproducible in both directions.
Each session is recorded as exactly one request. For a session that exchanged a turn, that request is the 409; for one that exchanged nothing, the same request is recorded as a success. So the error rate for a key doing real work sits at 100%.
The sessions themselves work. Audio flows, transcripts arrive, tool calls arrive, usage and token counts are recorded, and the WebSocket closes with code 1000. Nothing surfaces to the client and there is no error body anywhere. The dashboard is the only sign.
Minimal reproduction
Connect, send one text turn, wait for turnComplete, close. This records one 409. Removing the sendClientContent call - so the session only connects and closes - records none.
import { GoogleGenAI, Modality } from '@google/genai';
async function main(): Promise<void> {
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! });
let turnComplete = false;
const session = await ai.live.connect({
model: 'models/gemini-3.1-flash-live-preview',
config: {
responseModalities: [Modality.AUDIO],
systemInstruction: 'You are a terse assistant. Keep every reply to one short sentence.',
},
callbacks: {
onopen: () => console.log('open', new Date().toISOString()),
onmessage: (message) => {
if (message.serverContent?.turnComplete) {
turnComplete = true;
console.log('turn complete', new Date().toISOString());
}
},
onerror: (event) => console.log('error', event.message),
onclose: (event) => console.log('close', event.code, event.reason, new Date().toISOString()),
},
});
session.sendClientContent({ turns: [{ role: 'user', parts: [{ text: 'Say hello.' }] }], turnComplete: true });
for (let waited = 0; waited < 30_000 && !turnComplete; waited += 200) {
await new Promise((resolve) => setTimeout(resolve, 200));
}
session.close();
// Give the close handshake time to finish before the process exits.
await new Promise((resolve) => setTimeout(resolve, 3000));
}
void main();
Run it once, then read the "API errors" chart for that minute. The dashboard reflected it within a minute or two.
What was measured
Ten sessions, run standalone unless noted:
| Session |
What it did |
409 recorded |
| 3 x |
Connected, closed. Nothing sent. |
none |
| 2 x |
Sent one text turn, waited for turnComplete, closed. |
one each |
| 1 |
Sent one text turn, closed. Run in a process alongside two others. |
one |
| 1 |
Declared a hang_up function, prompted the model to call it, answered the call with sendToolResponse, closed. |
one |
| 2 |
Full two-way audio conversations of two and three minutes. |
one each |
Six sessions that exchanged a turn, six conflicts. Three that exchanged nothing, none. Every close was code 1000 with an empty reason, and every session appeared on the request chart exactly once.
What this rules out
- Not the tool call. A plain text turn is enough. The session that answered a
hang_up call with sendToolResponse before closing behaves exactly like the ones with no tools declared.
- Not closing while the model is mid-turn. The repro waits for
turnComplete before closing. A separate test waited 5 further seconds beyond that, and still recorded a conflict.
- Not one session colliding with the one before it. Two empty sessions opened 25 seconds apart recorded nothing at all, and the conversational sessions were minutes apart from anything else.
- Not the server rejecting or dropping the connection. Every close event is code
1000 with an empty reason - the client's own close, acknowledged normally.
Waiting does not end a session either: 5 seconds after the final turn there was still no close event, so the client is the one that has to close it.
Environment
@google/genai 2.17.1
- Node.js 22.23.2
- Model
models/gemini-3.1-flash-live-preview
- Gemini API key (AI Studio), not Vertex
Questions
- What does a 409 mean on the Live API, and what raises it? Nothing in a session's own lifecycle appears to fail, and a session that says nothing does not raise it at all.
- Is a client-initiated
session.close() the expected way to end a session that has been used? If not, what is?
- Does this count against quota, rate limits, or any health signal? An error logged for every session that does real work leaves nothing to alert on - a genuine failure looks the same as a normal day.
Related
What happens
Every Live API session that exchanges a turn records exactly one
409 Conflicton the "API errors" chart of the AI Studio usage dashboard. A session that connects and closes without sending anything records none - that is the line between the two, and it is reproducible in both directions.Each session is recorded as exactly one request. For a session that exchanged a turn, that request is the 409; for one that exchanged nothing, the same request is recorded as a success. So the error rate for a key doing real work sits at 100%.
The sessions themselves work. Audio flows, transcripts arrive, tool calls arrive, usage and token counts are recorded, and the WebSocket closes with code
1000. Nothing surfaces to the client and there is no error body anywhere. The dashboard is the only sign.Minimal reproduction
Connect, send one text turn, wait for
turnComplete, close. This records one 409. Removing thesendClientContentcall - so the session only connects and closes - records none.Run it once, then read the "API errors" chart for that minute. The dashboard reflected it within a minute or two.
What was measured
Ten sessions, run standalone unless noted:
turnComplete, closed.hang_upfunction, prompted the model to call it, answered the call withsendToolResponse, closed.Six sessions that exchanged a turn, six conflicts. Three that exchanged nothing, none. Every close was code
1000with an empty reason, and every session appeared on the request chart exactly once.What this rules out
hang_upcall withsendToolResponsebefore closing behaves exactly like the ones with no tools declared.turnCompletebefore closing. A separate test waited 5 further seconds beyond that, and still recorded a conflict.1000with an empty reason - the client's own close, acknowledged normally.Waiting does not end a session either: 5 seconds after the final turn there was still no close event, so the client is the one that has to close it.
Environment
@google/genai2.17.1models/gemini-3.1-flash-live-previewQuestions
session.close()the expected way to end a session that has been used? If not, what is?Related
gemini-3.1-flash-live-preview. This issue is that minimal repro.gemini-2.5-flash-native-audioandgemini-3.1-flash-live-preview. The staff reply there addresses a model deprecation rather than the 409.