google-gemini / google-gemini/gemini-cli
bug: auto-compression summarizer failure aborts the entire user turn instead of degrading
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
## What happened?
Auto-compression runs two auxiliary LLM calls (summarize + verify) through `BaseLlmClient.generateContent`, which internally uses `retryWithBackoff` and **throws** when attempts are exhausted. Neither `compress()`, nor `tryCompressChat()`, nor the call site in `processTurn()` wraps this in any error handling, so a transient failure of the *utility* compression model (a 500/timeout burning through its retry budget) propagates up and **fails the user's entire prompt**, even though the main model may be perfectly healthy.
The service already models graceful degradation for non-exception outcomes (`COMPRESSION_FAILED_EMPTY_SUMMARY`, `COMPRESSION_FAILED_INFLATED_TOKEN_COUNT`), which proves degradation is the intended semantic — but exceptions bypass it entirely.
## Affected code
`packages/core/src/context/chatCompressionService.ts:361-407` — the two unguarded calls:
```ts
const summaryResponse = await config.getBaseLlmClient().generateContent({ ... });
// ...
const verificationResponse = await config
.getBaseLlmClient()
.generateContent({ ... });
```
`packages/core/src/core/client.ts:1196-1214` — `tryCompressChat()` awaits `compress()` with no try/catch:
```ts
const { newHistory, info } = await this.compressionService.compress(
this.getChat(),
prompt_id,
force,
model,
this.config,
this.hasFailedCompressionAttempt,
abortSignal,
);
```
`packages/core/src/core/client.ts:689` — caller in `processTurn()`, also unguarded:
```ts
const compressed = await this.tryCompressChat(prompt_id, false, signal);
```
## How can this be reproduced?
1. Start a session large enough that auto-compression triggers at the context threshold.
2. Force the compression utility model to fail persistently (mock `BaseLlmClient.generateContent` to reject with a 5xx-shaped error so `retryWithBackoff` exhausts attempts).
3. Send the next message.
4. Observed: the turn throws; the user sees an error for their prompt.
5. Expected: compression is skipped, conversation continues uncompressed.
A unit test asserting `processTurn` still completes when the compressor rejects demonstrates this directly on current `main`.
## What did you expect to happen?
A failure of the auxiliary compression flow should degrade to "no compression this turn" (log a warning, set the existing failure flag), never take down the primary request path.
## Impact
Users on flaky networks / rate-limited projects get hard failures exactly when their context is fullest — the moment they can least afford to lose a turn.
## Suggested direction
Wrap the two `generateContent` calls in `compress()` (or `tryCompressChat()`) and map any exception to `COMPRESSION_FAILED_*` status, consistent with the existing graceful-failure statuses.
---
*Found by source audit on current `main` (commit `5411f113c`); platform-independent. No open issue/PR covering this was found (searched: compression tryCompressChat error).*
Contributor guide
Research direction
Start with packages/core/src/context/chatCompressionService.ts:361-407, then trace tryCompressChat() in packages/core/src/core/client.ts:1196-1214 and its processTurn() caller at line 689. Run a unit test that makes generateContent reject after retries are exhausted. Done means compression failure is logged and skipped, the existing failure state is set, and the user turn still completes uncompressed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- ai, cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100