google-gemini / google-gemini/gemini-cli
bug: shell execution runs exit handling twice on spawn failure ('error' and 'close' both call handleExit)
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
## What happened?
In the shell execution pipeline, both the child `'error'` and `'close'` events invoke `handleExit()`, and `handleExit` has no re-entrancy guard. Per Node's documented behavior, `'close'` fires after `'error'` when spawning fails — so every failed spawn runs exit handling **twice**: `onOutputEvent({ type: 'exit', ... })` fires twice, background-history finalization runs twice, and `cmdCleanup?.()` executes twice. If `'error'` fires mid-run on a successfully spawned child (e.g., kill/IPC failure), the result settles early on partial output before `'close'` arrives.
## Affected code
`packages/core/src/services/shellExecutionService.ts:819-838`:
```ts
child.on('error', (err) => {
error = err;
handleExit(1, null); // first settlement
});
// ...
child.on('close', (code, signal) => {
handleExit(code, signal); // second settlement for the same run
});
```
## How can this be reproduced?
Execute a command whose spawn fails (nonexistent executable / bad sandbox path). Observe two `exit` output events emitted for the single run (and doubled cleanup calls).
## What did you expect to happen?
Exactly one exit settlement per process run.
## Suggested direction
Add a `settled` flag inside `handleExit` (return immediately on second call), matching the pattern used elsewhere in this file for stream finalization.
---
*Found by source audit on current `main` (commit `5411f113c`); platform-independent. No open issue/PR covering this was found (searched: shell exit event twice).*
Contributor guide
Research direction
Start in packages/core/src/services/shellExecutionService.ts:819-838 and inspect how the child 'error' and 'close' handlers call handleExit. Reproduce the issue with a command whose spawn fails, then verify that one process run emits one exit event and performs cleanup and background-history finalization once.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100