Rejecting ExitPlanMode exits plan mode anyway

Resolved 💬 3 comments Opened Apr 7, 2026 by benlinton Closed Apr 11, 2026

Bug: Rejecting ExitPlanMode exits plan mode anyway

Summary

When the user rejects an ExitPlanMode tool call, plan mode is deactivated as if it was approved. The model then makes edits freely on subsequent turns with no plan mode enforcement.

Steps to Reproduce

  1. Enter plan mode via /plan
  2. Let the model complete planning and write to the plan file
  3. When the model calls ExitPlanMode, reject the tool call with a message like "I don't want to proceed with implementation"
  4. On the next turn, send a request that would require edits (e.g., "fix the paths in that file")
  5. Observe: the model calls Edit/Write/Bash tools without restriction

Expected Behavior

Rejecting ExitPlanMode should keep plan mode active:

  • The plan mode <system-reminder> should continue to be injected on all subsequent turns
  • Edit, Write, Bash (non-readonly), and other mutating tools should remain blocked
  • The model should only be able to edit the plan file
  • The user should need to explicitly re-enter and exit plan mode to unlock edits

Actual Behavior

After rejecting ExitPlanMode:

  • Plan mode <system-reminder> tags stop appearing on subsequent turns
  • All tools (Edit, Write, Bash, etc.) are unblocked
  • No error, warning, or indication that plan mode state changed
  • The model proceeds to make edits as if plan mode was never active

Root Cause Hypothesis

The plan mode state machine appears to treat any ExitPlanMode tool call — regardless of whether the user approved or rejected it — as a state transition out of plan mode. The rejection handler likely shares the same exit path as the approval handler, or the state is toggled when the tool is called rather than when the tool call is accepted.

Specifically, the system-reminder injection logic (which gates plan mode enforcement) seems to check a flag that gets cleared on ExitPlanMode invocation rather than on ExitPlanMode acceptance. Since the model itself cannot enforce plan mode (it relies on the system-reminder being present to know it's in plan mode), removing the reminder effectively removes all enforcement.

Conversation Flow That Triggered This

Turn 1: [plan mode active, system-reminder present]
  Model: writes plan file, calls ExitPlanMode
  User: rejects ExitPlanMode ("I don't want to proceed")

Turn 2: [plan mode system-reminder ABSENT — this is the bug]
  User: "fix the absolute paths"
  Model: calls Edit tool — succeeds, no restriction

Turn 3: [still no system-reminder]
  User: "create a new script"
  Model: calls Write, Bash — succeeds

Turns 4-8: [model makes ~15 edits across multiple files]
  No plan mode enforcement at any point

Turn 9: User notices and asks "how did you make changes in plan mode?"

Key Observations

  • The model cannot self-enforce plan mode. It relies entirely on the <system-reminder> injection. Once the reminder stops appearing, the model has no way to know it should still be in plan mode.
  • The bug persists across multiple turns — it's not a single-turn glitch.
  • Re-entering plan mode later works correctly (system-reminders reappear), confirming the injection mechanism itself works — it's only the rejection-of-exit path that's broken.
  • The plan file specified in the system-reminder remains accessible in later plan mode sessions, suggesting the plan file association is preserved even after the broken exit.

Suggested Fix

The ExitPlanMode rejection handler should:

  1. Not clear the plan mode flag — the state should remain plan_mode: true
  2. Continue injecting the plan mode system-reminder on subsequent turns
  3. Optionally: return a message to the model like "ExitPlanMode was rejected. You are still in plan mode." so the model can adjust its behavior

Severity

Medium-high. Plan mode is a trust/safety feature — users rely on it to review changes before they're made. When it silently deactivates, the model can make arbitrary file changes that the user didn't approve. In our case, ~15 file edits were made across 8 turns before the user noticed.

Environment

  • Claude Code CLI (latest as of 2026-04-07)
  • Model: Opus 4.6 (1M context)
  • macOS

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗