anthropics / anthropics/claude-code

[BUG] Desktop: Plan mode prompts for every non-read-only Bash command since 2.1.270 (plan+bypass quiet path now requires isInteractive; Desktop sessions get no quiet path)

Ouverte
#94,274 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
area:desktop area:permissions bug has repro platform:macos regression
Langage dominant
Python
Étoiles
145k
Forks
23.1k
Métriques de merge des PR
Métriques de PR en attente

Description

### Preflight Checklist

- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (2.1.270, as bundled in Claude Desktop 1.52386.6)

### What's Wrong?

In the Claude Desktop **Code tab** (macOS), switching a session to **Plan** mode now raises a permission card for **every Bash command that isn't in the built-in read-only set**. One session produced 35 Bash permission prompts in a single afternoon (desktop `main.log`: `LocalSessions.setPermissionMode … mode=plan` followed by a stream of `Emitted tool permission request … for Bash`). The same Desktop workflow on the previously bundled CLI (2.1.266, and 2.1.260 before it) produced **zero** Bash prompts in plan mode across several days of use.

Desktop launches the CLI as a non-interactive stream: `--output-format stream-json --input-format stream-json --permission-prompt-tool stdio`, plus `--permission-mode bypassPermissions --allow-dangerously-skip-permissions` when the Desktop setting **Allow bypass permissions mode** is on. Auto mode is available on the account and `useAutoModeDuringPlan` is at its default (on).

Comparing the two bundled binaries (`~/Library/Application Support/Claude/claude-code//claude.app/Contents/MacOS/claude`):

- **2.1.266** — the plan-mode "permissive" check at the permission-context resolver is inline:
`n.mode==="plan" && n.isBypassPermissionsModeAvailable===!0`
- **2.1.270** — the same call sites use a new helper:
`function zj(e,n){return e==="plan"&&n===!0&&!Ae()}` where `Ae()` is
`function Ae(){return!n().host.launchOptions.isInteractive()}`.
Desktop sessions are non-interactive, so the "bypass available → plan mode doesn't prompt" path is now switched off for every Desktop (and SDK / stream-json) session.
- The other quiet path — the auto-mode classifier reviewing commands during planning — is gated as
`e.mode==="auto" || e.mode==="plan" && () && !e.isBypassPermissionsModeAvailable`.
With the Desktop bypass toggle on, `isBypassPermissionsModeAvailable` is true for every session, so this path is off as well.

Net effect: in Desktop with the bypass toggle on, **plan mode has no non-prompting path at all**; every non-read-only shell command asks the user. A terminal session with identical settings is unaffected because `isInteractive()` is true there.

The docs (Permission modes → "Skip all checks with bypassPermissions mode") still state: *"In sessions with bypass permissions available, Claude Code also doesn't enforce plan mode's blocks… a file edit or shell command it attempts during planning runs without prompting."* That no longer holds for Desktop/SDK sessions. Nothing in the 2.1.267–2.1.270 CHANGELOG entries mentions plan mode.

### What Should Happen?

One of the two documented quiet paths should apply to Desktop plan mode:

1. plan mode does not enforce its blocks when bypass permissions are available (behavior through 2.1.266, and what the docs describe), **or**
2. if gating that path on interactive sessions is intentional, non-interactive sessions should fall through to the classifier-during-planning path instead of to a prompt (i.e. `!isBypassPermissionsModeAvailable` should not exclude the classifier path when the bypass path itself is unavailable).

If the change is deliberate (e.g. non-interactive sessions must not run commands silently in plan mode), it should be in the changelog and docs, and the Desktop mode selector should not offer a Plan mode that prompts on everything.

### Error Messages/Logs

Desktop `~/Library/Logs/Claude/main.log` (ids elided):

```
2026-09-14 15:46:12 [info] [CCD] LocalSessions.setPermissionMode: sessionId=local_…, mode=plan
2026-09-14 15:46:12 [info] [CCD] Set permission mode for session local_… to plan
2026-09-14 15:58:19 [info] Emitted tool permission request e89ff78f-… for Bash in session local_…
2026-09-14 15:58:23 [info] LocalSessions.respondToToolPermission: requestId=e89ff78f-…, decision=once, hasUpdatedInput=true
2026-09-14 15:58:23 [info] Received permission response for e89ff78f-…: once (tool: Bash)
… 35 Bash permission requests between 15:58 and 16:15 in that one plan-mode session
```

Plan-mode toggles vs Bash permission prompts, same Desktop app and same workflow, counted from the log:

| date | bundled CLI | plan mode entered | Bash permission prompts |
| --- | --- | --- | --- |
| 2026-09-08 | 2.1.260 | 2× via mode selector | 0 |
| 2026-09-09 | 2.1.260 | 3× via mode selector | 0 |
| 2026-09-13 | 2.1.266 | via EnterPlanMode (ExitPlanMode prompt seen) | 0 |
| 2026-09-14 | 2.1.270 | 3× via mode selector (6 mode changes total) | 35 |

### Steps to Reproduce

1. Claude Desktop for macOS 1.52386.6 with bundled Claude Code 2.1.270. Desktop settings: **Allow bypass permissions mode** ON. Auto mode available (Max plan).
2. Code tab → open a session in any folder; mode selector → **Bypass permissions** (or Auto). Confirm shell commands run without prompts.
3. Switch the mode selector to **Plan**.
4. Ask for research that needs non-read-only shell commands (e.g. "inspect X with `ssh`/`gh`/`python3` and write a plan"; long compound commands that the read-only analysis can't classify also qualify).
5. Every such Bash call raises a permission card ("once" / "always" choices). With the previous bundled CLI (2.1.266) the same steps run the commands without prompting.

Code path: `strings` the 2.1.270 binary for `function zj(e,n){return e==="plan"&&n===!0&&!Ae()}` and `function Ae(){return!n().host.launchOptions.isInteractive()}`; in 2.1.266 the corresponding check is `n.mode==="plan"&&n.isBypassPermissionsModeAvailable===!0`.

### Claude Model

claude-fable-5-1 in the affected session (also seen with claude-opus-4-8). Model-independent.

### Is this a regression?

Yes, this worked in a previous version

### Last Working Version

2.1.266 (Claude Code), bundled in the same Desktop app until 2026-09-14. (2.1.267–2.1.269 not tested.)

### Claude Code Version

2.1.270 (Claude Code), bundled in Claude Desktop 1.52386.6

### Platform

Anthropic API (claude.ai Max subscription through the Desktop app)

### Operating System

macOS (26.6.2)

### Terminal/Shell

Other (Claude Desktop app, Code tab — non-interactive stream-json host)

### Additional Information

Workarounds identified (not a fix):

- Add `"Bash(*)"` to `permissions.allow` in `~/.claude/settings.json`. Per the permissions docs, plan mode sends non-read-only commands through the regular permission flow, which honors allow rules, while edits stay blocked. Not yet verified end to end.
- Turn the Desktop **Allow bypass permissions mode** toggle off. Bypass then stops being "available", so the classifier-during-planning path engages. Costs the Bypass mode entirely.

Possibly related but distinct: #10906 (Plan subagent ignores parent permissions), #89403 (plan mode gating after a rejected ExitPlanMode).

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Piste de recherche

Start at the permission-context resolver and the non-interactive launchOptions.isInteractive() path described in the bundled 2.1.270 binary, then compare it with 2.1.266. Reproduce with Claude Desktop stream-json sessions and the documented Permission modes behavior; done means one documented quiet path works in Plan mode or the intentional behavior is reflected in the changelog, docs, and mode selector.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python, shell
Domaine
authorization, cli, developer-experience
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
Active
Clarté
Plutôt claire
Accessibilité débutants
48/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.