google-gemini/gemini-cli · warning
MAX_TURNS_EXCEEDED
MAX_TURNS_EXCEEDED
Error message
MAX_TURNS_EXCEEDED
What it means
MAX_TURNS_EXCEEDED is emitted as an agent_end event (reason 'max_turns') when the Gemini stream reports GeminiEventType.MaxSessionTurns. It is the protocol-level signal that the server-side agent loop hit its turn budget. It is a terminal-but-successful stream end, not a thrown exception in the translator.
Source
Thrown at packages/core/src/agent/event-translator.ts:167
handleError(event.value.error, state, out);
break;
case GeminiEventType.UserCancelled:
ensureStreamStart(state, out);
out.push(
makeEvent('agent_end', state, {
reason: 'aborted',
}),
);
break;
case GeminiEventType.MaxSessionTurns:
ensureStreamStart(state, out);
out.push(
makeEvent('agent_end', state, {
reason: 'max_turns',
data: {
code: 'MAX_TURNS_EXCEEDED',
},
}),
);
break;
case GeminiEventType.LoopDetected:
ensureStreamStart(state, out);
out.push(
makeEvent('error', state, {
status: 'INTERNAL',
message: 'Loop detected, stopping execution',
fatal: false,
_meta: { code: 'LOOP_DETECTED' },
}),
);
break;
case GeminiEventType.ContextWindowWillOverflow:View on GitHub (pinned to 5024443c72)
Solutions
- Raise the turn budget: config.setMaxSessionTurns() / server-side policy.
- Inspect prior tool_request/tool_response events to find where the loop stalled and fix the failing tool.
- Re-prompt with a more specific task so fewer turns are needed.
Example fix
// before config.setMaxSessionTurns(20); // after config.setMaxSessionTurns(80);
Defensive patterns
Strategy: validation
Validate before calling
// before launching the agent
const max = config.getMaxSessionTurns();
if (typeof max === 'number' && max >= 0 && max < expectedTurns) {
config.setMaxSessionTurns(Math.max(max, expectedTurns));
} Type guard
function isMaxTurnsData(data: unknown): data is { code: 'MAX_TURNS_EXCEEDED'; maxTurns: number; turnCount: number } {
return typeof data === 'object' && data !== null && (data as any).code === 'MAX_TURNS_EXCEEDED';
} Try / catch
// agent_end event with reason 'max_turns' is not thrown; handle in the stream consumer:
for await (const ev of session) {
if (ev.type === 'agent_end' && ev.reason === 'max_turns') { /* surface + retry with higher budget */ }
} Prevention
- Size maxTurns to the task (multi-file edits need more turns).
- Log turnCount vs maxTurns on agent_end to detect chronic under-budgeting.
When it happens
Trigger: translateEvent() receives an event of type GeminiEventType.MaxSessionTurns from sendMessageStream -> pushes an agent_end event with data.code 'MAX_TURNS_EXCEEDED'.
Common situations: Long agentic tasks (multi-file refactors, deep debugging) where the model chains many tool calls; tight maxTurns config; a tool keeps failing so the model retries indefinitely until the server cap trips.
Related errors
- LOOP_DETECTED
- MAX_TURNS_EXCEEDED
- AGENT_EXECUTION_BLOCKED
- INVALID_STREAM
- Failed to create chat object: ${getErrorMessage(e)}
AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12).
Data as JSON: /api/errors/e823958af21796e9.
Report an issue: GitHub.