Skip to content

Bug: Fatal CLI Crash on Loop Detection via Unhandled AbortError #20106

@hsm207

Description

@hsm207

What happened?

The Gemini CLI hard-crashes with a synchronous AbortError when the LLM generates repetitive content that triggers the internal LoopDetectionService. This occurs in both interactive and non-interactive (headless) modes.

The crash is caused by a synchronous call to controller.abort() within the streaming loop of GeminiClient.processTurn. The @google/genai SDK wraps the underlying node-fetch stream in a custom async iterator (ReadableStreamToAsyncIterable). This custom wrapper detaches its 'error' listeners from the stream during yield suspensions (i.e., while the consuming loop is processing a chunk).

When abort() is called in this state, node-fetch synchronously emits an 'error' event. Finding no active listeners, Node.js triggers a fatal uncaught exception. Node's native AbortSignal (via EventTarget) intercepts this throw and schedules a fatal process.nextTick error, which bypasses all high-level try...catch blocks and terminates the process (Exit Code 1).

Furthermore, this crash renders the CLI's interactive confirmation logic unreachable, as the process terminates before the LoopDetected event can be effectively rendered by the UI layer.

Reproduction Steps

  1. Clone the repository and install dependencies (npm install).
  2. Run the initial build: npm run build.
  3. Execute the following command to trigger the crash:
npm start -- -p "Repeat the following sequence exactly 50 times: la li lu le lo"

Stack Trace

AbortError: The operation was aborted.
    at AbortController.abort (node:internal/abort_controller:507:5)
    at GeminiClient.processTurn (packages/core/src/core/client.ts)
    ...

What did you expect to happen?

The application should identify the loop, yield a LoopDetected event to the UI, and either:

  1. Stop the stream gracefully without crashing the process.
  2. Allow the CLI's interactive confirmation dialog to appear, enabling the user to choose whether to continue or disable loop detection for the session.

Client information

  • CLI Version: 0.30.0-nightly.20260210.a2174751d
  • Platform: Linux
  • Node.js Version: v20.x+

Login information

Authenticated via cached credentials (OAuth/API Key).

Anything else we need to know?

This issue is a regression introduced in Commit 5a05fb0 (PR #8377), which added the controller.abort() call to save tokens but omitted the necessary safeguards for the resulting signal propagation in mixed-environment dependencies.

Related Issues

  • Issue #10952: Documents the emergence of the AbortError crash in v0.8.1+.
  • Issue #18028: Highlights the Ink UI's vulnerability to hard crashes when encountering unhandled exceptions like this one.
  • Issue #4878: Confirms that the system previously handled loops gracefully in v0.1.13 without crashing.

I have identified an approach that resolves this crash by ensuring the system exits the streaming loop before any abort logic is triggered. I'll be opening a PR shortly to implement this fix and am looking forward to discussing the implementation and any potential improvements with the team there!

Metadata

Metadata

Assignees

Labels

area/agentIssues related to Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Qualityhelp wantedWe will accept PRs from all issues marked as "help wanted". Thanks for your support!

Type

No fields configured for Bug.

Projects

Status

Closed

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions