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

  1. Raise the turn budget: config.setMaxSessionTurns() / server-side policy.
  2. Inspect prior tool_request/tool_response events to find where the loop stalled and fix the failing tool.
  3. 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

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


AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12). Data as JSON: /api/errors/e823958af21796e9. Report an issue: GitHub.