Skip to content

fix(core): remove temp files on background process exit#28394

Open
Daksh7785 wants to merge 2 commits into
google-gemini:mainfrom
Daksh7785:fix/shell-bg-temp-leak
Open

fix(core): remove temp files on background process exit#28394
Daksh7785 wants to merge 2 commits into
google-gemini:mainfrom
Daksh7785:fix/shell-bg-temp-leak

Conversation

@Daksh7785

Copy link
Copy Markdown

Fix: Temporary Directory Leak During Background Shell Execution

🐛 Description

This PR fixes a resource leak where the CLI permanently leaves behind temporary directories in the host OS's temp folder whenever a shell command is executed with is_background: true.

The temporary directory (e.g. gemini-shell-*) is used to store bgpids.tmp for background process tracking. Previously, cleanup was intentionally skipped because the background process could still be writing to the file, but ownership of the directory was never transferred, leaving it orphaned after the process exited.

🛠️ Solution

This change uses the existing ShellExecutionService.onExit lifecycle hook to perform cleanup when the background process terminates.

  • Foreground executions continue to clean up immediately in the finally block (no behavior change).
  • Background executions register a cleanup callback with ShellExecutionService.onExit.
  • Once the background process exits, the callback removes the temporary directory and its contents.

This preserves the current background execution behavior while ensuring temporary resources are always cleaned up.

📝 Changes

  • packages/core/src/tools/shell.ts

    • Register a cleanup callback with ShellExecutionService.onExit for background executions.
    • Preserve existing cleanup logic for foreground executions.
  • packages/core/src/tools/shell.test.ts

    • Mock ShellExecutionService.onExit in unit tests to avoid TypeErrors and verify the new behavior.

✅ Verification

  • Ran npm test -w @google/gemini-cli-core.
  • All 89 tests pass successfully.
  • Verified that background (is_background: true) shell executions register the cleanup callback without affecting existing functionality.
  • Confirmed that temporary directories are removed after the background process exits.

🔗 Related Issue

Fixes #28392

Fixes google-gemini#28392 by registering an exit callback on the ShellExecutionService.
@Daksh7785 Daksh7785 requested a review from a team as a code owner July 13, 2026 18:17
@google-cla

google-cla Bot commented Jul 13, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

🛑 Action Required: Evaluation Approval

Steering changes have been detected in this PR. To prevent regressions, a maintainer must approve the evaluation run before this PR can be merged.

Maintainers:

  1. Go to the Workflow Run Summary.
  2. Click the yellow 'Review deployments' button.
  3. Select the 'eval-gate' environment and click 'Approve'.

Once approved, the evaluation results will be posted here automatically.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a resource leak where temporary directories created for background shell processes were not being removed after process termination. By leveraging the existing onExit lifecycle hook, the system now ensures that these temporary files and directories are properly cleaned up once the background process exits, preventing unnecessary disk usage.

Highlights

  • Temporary Directory Cleanup: Implemented automatic cleanup of temporary directories for background shell executions by registering a callback with the ShellExecutionService.onExit lifecycle hook.
  • Test Coverage: Updated unit tests to mock the new onExit functionality, ensuring compatibility and preventing regressions in shell execution logic.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions Bot added the size/s A small PR label Jul 13, 2026
@github-actions

Copy link
Copy Markdown

📊 PR Size: size/S

  • Lines changed: 21
  • Additions: +21
  • Deletions: -0
  • Files changed: 2

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces cleanup logic for temporary files and directories when shell commands are executed in the background by registering an onExit handler. The review feedback highlights a potential resource leak if the background process fails to spawn or if an error occurs before the onExit handler is registered, and provides a code suggestion to ensure cleanup is safely handled in the finally block for these failure scenarios.

Comment on lines +698 to +715
if (tempFilePath || tempDir) {
ShellExecutionService.onExit(pid, () => {
if (tempFilePath) {
try {
fs.unlinkSync(tempFilePath);
} catch {
// Ignore
}
}
if (tempDir) {
try {
fs.rmSync(tempDir, { recursive: true, force: true });
} catch {
// Ignore
}
}
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a potential resource leak if the background process fails to spawn or if an error is thrown before the onExit handler is registered. In those cases, onExit is never registered, and the finally block (lines 1088-1103) will skip cleanup because this.params.is_background is true.

To fix this completely, we should clear tempFilePath and tempDir once the cleanup is successfully deferred to onExit, and update the finally block to clean up any remaining non-empty paths:

// In the finally block (around line 1088):
if (!this.params.is_background || tempFilePath || tempDir) {
  if (tempFilePath) {
    try {
      await fsPromises.unlink(tempFilePath);
    } catch {
      // Ignore errors during unlink
    }
  }
  if (tempDir) {
    try {
      await fsPromises.rm(tempDir, { recursive: true, force: true });
    } catch {
      // Ignore errors during rm
    }
  }
}
          if (tempFilePath || tempDir) {
            const fileToClean = tempFilePath;
            const dirToClean = tempDir;
            ShellExecutionService.onExit(pid, () => {
              if (fileToClean) {
                try {
                  fs.unlinkSync(fileToClean);
                } catch {
                  // Ignore
                }
              }
              if (dirToClean) {
                try {
                  fs.rmSync(dirToClean, { recursive: true, force: true });
                } catch {
                  // Ignore
                }
              }
            });
            tempFilePath = '';
            tempDir = '';
          }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Issues related to User Interface, OS Support, Core Functionality size/s A small PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Temporary Directory Leak During Background Shell Execution

1 participant