[BUG] Plan mode fails to restrict write tools when isBypassPermissionsModeAvailable is true
Bug Description
When isBypassPermissionsModeAvailable is true (e.g., user has allowDangerouslySkipPermissions configured or started with --dangerously-skip-permissions), plan mode fails to block write operations. The model can call Edit, Write, and Bash tools during plan mode and they are auto-approved without prompting, defeating the purpose of plan mode.
Steps to Reproduce
- Start Claude Code with bypass permissions available (e.g.,
claude --dangerously-skip-permissions) - Enter plan mode via Shift+Tab or
/plan - Give Claude a complex task — it enters plan mode
- Observe: if the model calls
Edit,Write, orBash(e.g., to "test something" or starts implementing prematurely), those calls are auto-approved without showing a permission dialog - Expected: Plan mode should restrict all write operations to read-only exploration, regardless of bypass availability
- Actual: Write tools execute silently because the bypass check treats plan mode + bypass-available as equivalent to full bypass
Root Cause (source-level analysis)
File: src/utils/permissions/permissions.ts — step 2a of the permission check pipeline:
// Line ~1268
const shouldBypassPermissions =
appState.toolPermissionContext.mode === 'bypassPermissions' ||
(appState.toolPermissionContext.mode === 'plan' &&
appState.toolPermissionContext.isBypassPermissionsModeAvailable)
if (shouldBypassPermissions) {
return {
behavior: 'allow',
updatedInput: getUpdatedInputOrFallback(toolPermissionResult, input),
decisionReason: { type: 'mode', mode: appState.toolPermissionContext.mode },
}
}
The second condition (mode === 'plan' && isBypassPermissionsModeAvailable) auto-approves all tools — including Edit, Write, and Bash — when bypass is merely available (configured). The isBypassPermissionsModeAvailable flag indicates that bypass permissions have been configured, not that the user is currently in bypass mode.
No other layer blocks write tools in plan mode
tools.tsandtoolPool.tsdo not filter or disable write tools when plan mode is active- Plan mode's read-only behavior is enforced only via system prompt guidance in
messages.ts(~line 3331) andEnterPlanModeTool.ts(~line 104) - If the model ignores the system prompt and calls a write tool, there is no hard permission gate to catch it
What IS properly guarded
ExitPlanMode itself is correctly guarded — it has a requiresUserInteraction() check (ExitPlanModeV2Tool.ts ~line 185) that returns 'ask' before the bypass check at step 2a runs. So the ExitPlanMode dialog still shows. The problem is specifically with ordinary write tools.
Suggested Fix
Restrict the plan-mode bypass to read-only tools and plan-file operations:
const shouldBypassPermissions =
appState.toolPermissionContext.mode === 'bypassPermissions' ||
(appState.toolPermissionContext.mode === 'plan' &&
appState.toolPermissionContext.isBypassPermissionsModeAvailable &&
(tool.isReadOnly === true || isPlanFileOperation(tool, input)))
This preserves the UX benefit (no prompts for Read, Grep, Glob during planning) while ensuring Edit, Write, and Bash still require approval in plan mode.
Additionally, the duplicated plan-mode assumption in speculation.ts (~line 465) should be updated for consistency.
Related Issues
- #32934 — ExitPlanMode fails with
--dangerously-skip-permissions(Shift+Tab entry path) - #30463 — Plan mode blocks execution in don't-ask mode (deadlock)
- #29064 — Agent teammates stuck in plan-mode approval loops
- #29110 — bypassPermissions ineffective + plan mode loop in spawned agents
Environment
- Claude Code version: 2.1.88+
- Source reference: https://github.com/sanbuphy/claude-code-source-code
- macOS Darwin 25.3.0
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗