Skip to content

Race Condition in Policy Persistence leads to ENOENT error #18504

@braddux

Description

@braddux

What happened?

Description

The Gemini CLI fails to persist policy updates (e.g., when a user selects "Always allow" for a tool) because of a race condition in the atomic write logic. Multiple concurrent UPDATE_POLICY events cause fs.rename to fail with ENOENT because the fixed temporary filename is overwritten or moved by a competing process/event.

Steps to Reproduce

  1. Open a Gemini CLI session.
  2. Execute a task that triggers multiple tool confirmation prompts (e.g., using a sub-agent that runs several shell commands).
  3. Rapidly click "Always allow" for multiple commands, or have multiple sessions/sub-agents update policies simultaneously.
  4. The CLI will output an error: Failed to persist policy for run_shell_command.

Observed Behavior (Logs)

✖ Failed to persist policy for run_shell_command
Error: ENOENT: no such file or directory, rename '/gemini/policies/auto-saved.toml.tmp' -> '/.gemini/policies/auto-saved.toml'
at async Module.rename (node:internal/fs/promises:784:10)
at async MessageBus. (gemini-cli-core/dist/src/policy/config.js:347:17)

Root Cause Analysis

  • The issue is located in gemini-cli-core/src/policy/config.js within the createPolicyUpdater function.
  • The code uses a fixed temporary filename and lacks a locking mechanism to serialize writes:

// From gemini-cli-core/dist/src/policy/config.js

   const tmpFile = `${policyFile}.tmp`; // <--- Problem: Fixed filename
   await fs.writeFile(tmpFile, newContent, 'utf-8');
   await fs.rename(tmpFile, policyFile); // <--- Problem: Race condition here

The Failure Sequence:

  1. Event A starts: Reads auto-saved.toml, creates auto-saved.toml.tmp.
  2. Event B starts (concurrently): Reads auto-saved.toml, overwrites auto-saved.toml.tmp.
  3. Event B finishes: Renames auto-saved.toml.tmp to auto-saved.toml.
  4. Event A attempts to finish: Calls fs.rename on auto-saved.toml.tmp, but the file is already gone (moved by Event B).
  5. Result: Event A throws ENOENT.

What did you expect to happen?

The auto-saved.toml file should be updated.

Client information

Client Information

Run gemini to enter the interactive CLI, then run the /about command.

> /about
│ CLI Version                                                                      0.28.0-preview.5
│ Git Commit                                                                       44c67daa0
│ Model                                                                            auto-gemini-3
│ Sandbox                                                                          no sandbox
│ OS                                                                               linux 
│ Auth Method                                                                      Logged in with Google
│ GCP Project                                                                      shared-g3-gemini-quota

Login information

google account

Anything else we need to know?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/enterpriseIssues related to Telemetry, Policy, Quota / Licensingstatus/need-triageIssues that need to be triaged by the triage automation.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions