google-gemini / google-gemini/gemini-cli
File descriptor leak in ShellExecutionService.background() when command exits before 200ms delay
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
### What happened?
In packages/core/src/services/shellExecutionService.ts, the static background(pid) method (line 1197) unconditionally creates an fs.WriteStream for background logging and adds the PID to backgroundLogPids/backgroundLogStreams BEFORE calling ExecutionLifecycleService.background(pid) at line 1226.
When a command marked is_background: true exits before the 200ms BACKGROUND_DELAY_MS timer fires, the following sequence occurs:
The process exit handler fires, calling cleanupLogStream(pid) at line 625 (child_process) or line 1059 (PTY). This cleans up any existing log stream for the PID.
ExecutionLifecycleService.settleExecution() runs, deleting the active resolver and active execution entry.
200ms later, ShellExecutionService.background(pid) fires.
It creates a NEW fs.WriteStream (line 1206) and registers it in backgroundLogStreams (line 1210).
It adds the PID to backgroundLogPids (line 1224).
It calls ExecutionLifecycleService.background(pid) (line 1226), which returns early at line 412 because the resolver was already deleted by settleExecution.
The newly created WriteStream is now permanently leaked because:
cleanupLogStream already ran during the process exit event.
Nobody will call cleanupLogStream again for this PID.
The stream remains open in backgroundLogStreams indefinitely.
Root Cause: ShellExecutionService.background() has no mechanism to know whether ExecutionLifecycleService.background() succeeded or failed. The log stream setup and the lifecycle state change are not atomic. ExecutionLifecycleService.background() returns void, providing no feedback.
### What did you expect to happen?
ShellExecutionService.background() should only create the log stream and register the PID if the lifecycle transition actually succeeded. ExecutionLifecycleService.background() should return a boolean indicating whether backgrounding was successful, and the log stream creation should be gated on that return value.
### Client information
CLI Version: 0.35.0-nightly.20260313.bb060d7a9 Node Version: v20.19.0 OS: Windows_NT (Windows 11) Architecture: x64
### Login information
Signed in with Google Account via OAuth flow Email:
kumaradithyabathula@gmail.com
### Anything else we need to know?
I have a validated fix ready. It modifies two files:
ExecutionLifecycleService.background() return type changed from void to boolean (returns false on early exit, true on success).
ShellExecutionService.background() gates all log stream creation and PID registration on the boolean return value.
Unit tests have been written for both the lifecycle service (verifying boolean returns) and the shell execution service integration. The fix is backward-compatible. Let me know if you would like me to push up a PR.
Contributor guide
Assessment
This issue has not been assessed yet.