google-gemini / google-gemini/gemini-cli
Aborted streaming responses discard already-received usageMetadata
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
### What happened?
When a streaming response is aborted (Ctrl-C, timeout, or signal.abort), `Turn.run` yields `UserCancelled` and returns before emitting the `Finished` event (`packages/core/src/core/turn.ts:295-298`). The `Finished` event is the only place `usageMetadata` is emitted to consumers (`turn.ts:405-413`). Additionally, `LoggingContentGenerator` captures `lastUsageMetadata` from each received chunk (`loggingContentGenerator.ts:544-546`) but only flushes it via `_logApiResponse` on the success path (`loggingContentGenerator.ts:551`); the catch block for `AbortError` at `loggingContentGenerator.ts:584` skips the flush entirely.
So when a stream is aborted after content has been generated, the already-received usage metadata is discarded. Telemetry, logging, and token-usage displays show nothing for the aborted turn, even though the provider billed the partial generation.
The repo's own test confirms the abort path yields no Finished event: `turn.test.ts:199` asserts events = `[Content, UserCancelled]`.
### What did you expect to happen?
Usage metadata already received from the provider during streaming should be preserved and recorded even when the stream is aborted, not silently dropped.
### Client information
Source-level bug verified on `main` at `ac42fb0`. Mechanism confirmed by the repo's own test suite (`turn.test.ts:199`) and code inspection. Platform: macOS.
### Anything else we need to know?
The fix could flush `lastUsageMetadata` (already captured per-chunk) into telemetry on the abort path, marking the record as `aborted=true` to distinguish partial from complete usage.
Contributor guide
Research direction
Start with the abort and Finished-event paths in packages/core/src/core/turn.ts:295-298 and 405-413, then inspect lastUsageMetadata handling in loggingContentGenerator.ts:544-546, 551, and 584. Run the abort case in turn.test.ts:199 first; done means already-received usage metadata is recorded for aborted streams and the partial record is distinguishable as aborted.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- ai, cli, observability-sre
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 75/100