Skip to content

Plan Mode Write Operations Denied When sessionId is Missing #21359

@jerop

Description

@jerop

In Plan Mode, the write_file and replace tools fail repeatedly when trying to save plans if the sessionId is missing or undefined. This occurs because the security policy for Plan Mode enforces a strict directory structure that doesn't account for paths without a session-specific subdirectory.

Reproduction Steps:

  1. Initialize a session where the sessionId is not provided to the Storage or Config constructor (e.g., during certain resumed sessions, subagent calls, or in testing environments).
  2. Enter Plan Mode.
  3. Attempt to save a plan using write_file or replace.

Actual Behavior:
The tool execution is denied by the Policy Engine with the following message:

"You are in Plan Mode and cannot modify source code. You may ONLY use write_file or replace to save plans to the designated plans directory as .md files."

The agent fails to save the plan and often enters a loop of retrying the same failed operation.

Expected Behavior:
The agent should be able to save plans to the designated plans directory regardless of whether a sessionId subdirectory is present, provided the path remains within the project's temporary directory.

Technical Analysis:
The root cause is a discrepancy between how Storage generates paths and how the security policy validates them:

  1. Strict Policy Regex: The plan.toml policy file uses a regular expression to allow writes to the plans directory:

    argsPattern = "\"file_path\":\"[^\"]+[\\\\/]+\\.gemini[\\\\/]+tmp[\\\\/]+[\\w-]+[\\\\/]+[\\w-]+[\\\\/]+plans[\\\\/]+[\\w-]+\\.md\""

    This regex requires exactly two subdirectories after tmp/ (representing both the <projectIdentifier> and the <sessionId>).

  2. Path Generation: When sessionId is missing, Storage.getProjectTempPlansDir() returns a path with only one subdirectory after tmp/:
    ~/.gemini/tmp/<projectIdentifier>/plans/

Because this path only has one level of nesting instead of two, it fails the argsPattern check. The request then falls through to the "Catch-All Deny" rule in the policy, leading to the failure.

Metadata

Metadata

Assignees

Labels

area/coreIssues related to User Interface, OS Support, Core Functionalityarea/enterpriseIssues related to Telemetry, Policy, Quota / Licensingworkstream-rollupLabel used to tag epics and features that are associated with one of the three primary workstreams

Type

No fields configured for Task.

Projects

Status

Closed

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions