[BUG] Claude Desktop: permissions.defaultMode=bypassPermissions in settings.json stopped being honored (regression — worked previously)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Environment:
- Windows 11 Pro
- Running inside Claude Desktop (env var CLAUDE_CODE_ENTRYPOINT=claude-desktop)
- CLAUDE_AGENT_SDK_VERSION=0.3.202
Issue:
For many past sessions, setting "defaultMode": "bypassPermissions" in
.claude/settings.json worked correctly and permission prompts did not appear.
As of this week, permission prompts appear on ordinary tool calls (e.g. a
simple Bash command) despite this setting being correctly present.
Confirmed facts:
- "permissions": { "defaultMode": "bypassPermissions" } is set in BOTH
~/.claude/settings.json (user/global) and the project's
.claude/settings.local.json
- Both files are valid JSON (parsed successfully, no syntax errors)
- No managed-settings.json exists anywhere on the machine (checked all
standard Windows locations: C:\ProgramData\ClaudeCode\,
%APPDATA%\Claude\, %APPDATA%\Claude Code\, %LOCALAPPDATA%\ClaudeCode\,
%LOCALAPPDATA%\Claude\ — none present, so this is not an enterprise
policy override)
- The app UI shows a "Manual permissions" label, indicating the session
is actually running in the default/manual (ask-every-time) mode, not
bypassPermissions
- Problem persists across brand-new sessions started after confirming
the settings files — this is not a stale-session/config-not-reloaded
issue
- No UI control was found (via Shift+Tab or elsewhere) to manually
switch the session's permission mode from inside Claude Desktop
Ask:
Please confirm whether Claude Desktop is expected to read
permissions.defaultMode from settings.json the same way the terminal
CLI does, and if so, why it is not taking effect. This previously
worked reliably across many sessions and has regressed.
What Should Happen?
Claude Desktop should honor "defaultMode": "bypassPermissions" set in
settings.json and skip permission prompts, the same way it did across
many previous sessions before this regressed.
Error Messages/Logs
Steps to Reproduce
- Set "permissions": { "defaultMode": "bypassPermissions" } in both
~/.claude/settings.json (global) and the project's
.claude/settings.local.json
- Confirm both files are valid JSON (no syntax errors)
- Start a brand-new session in Claude Desktop on Windows
- Ask Claude to run any ordinary Bash command (e.g.
command -v ffmpeg) - A permission prompt ("Allow Claude to run X?" with Deny / Always
allow / Allow once) appears anyway — even though defaultMode is set
to bypassPermissions and no managed-settings.json exists anywhere
on the machine (checked all standard Windows locations)
This exact settings-file configuration worked correctly (no prompts)
across many prior sessions. The behavior has regressed recently.
Claude Model
Sonnet (default)
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
Sonnet 5
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
This looks like a mode-resolution bug, but I would avoid treating it as only a settings reload question.
The useful diagnostic boundary is: configured permission mode, managed policy override, entrypoint, UI label, and effective tool execution behavior should all resolve through one auditable chain.
A regression test could cover:
If the desktop surface intentionally differs from the terminal CLI, the product should expose that as an explicit override source rather than silently falling back to manual permissions.
The important invariant is that the user should be able to distinguish "the setting was not read", "the setting was overridden", "the setting was read but not supported in this entrypoint", and "the UI label/runtime behavior is stale."
That gives maintainers a narrow acceptance boundary: permission mode is not just config text; it is a bound state transition from source config to effective runtime authority.
Boundary: diagnostic/regression-test feedback only; no claim about Anthropic fix, Claude Code adoption, implementation correctness, official alignment, partnership, customer interest, integration, or Neura usage.
Adding a mechanism for this, traced through the shipped Desktop bundle. I think this
explains the whole cluster (#61436, #61958, #61860, #62076, #61415, #55095, #77555) —
none of which currently name a cause, which I suspect is why #77555 was closed as not
planned despite a clean config dump.
Environment: macOS 27.0, Claude Desktop 1.24012.9, bundled Claude Code 2.1.219,
individual Max plan, local session (not Cowork/remote/cloud).
Config is not the cause. On my machine every gate is already correct:
| surface | value |
|---|---|
|
~/.claude/settings.jsonpermissions.defaultMode|bypassPermissions|| desktop pref
bypassPermissionsModeEnabled|true|| desktop pref
bypassPermissionsGateByAccount[<uuid>]|true|| desktop pref
bypassPermissionsOptInByAccount[<uuid>]|true||
epitaxy-folder-permission-mode.<uuid>.<cwd>|bypassPermissions|| live session argv |
--permission-mode bypassPermissions --allow-dangerously-skip-permissions|| GrowthBook
tengu_disable_bypass_permissions_mode|false||
managed-settings.json| absent (both macOS paths) |Note this differs from #62084: the
--dangerously-skip-permissionsflag is beingpassed here. The launch is correct and the mode is still refused.
Mechanism — a not-yet-loaded config is treated as a denial.
In
Contents/Resources/ion-dist/assets/v1/index-C9LAU4f-*.js, the bypass-availabilityhelper builds its gating object as:
and evaluates it with:
The landing composer in
c360a9e1c-*.jsthen consumes it as:So
configLoadingproducesundefined, and?? falseconverts that into a hard"not available." The sibling
errorCategoryhelper does the same thing:nullmatches none of the renderer's known categories (root_detected,ant_account,pref_disabled), so it falls through to the generic toast — "Bypass permissions isn'tavailable for this session. Asking for permissions instead." — and clamps to
default.Reproduction (confirmed on my machine, 2026-07-29):
mode clamps to Manual.
Start another new session → banner appears again, identical behavior.
correctly reports Bypass Permissions.
Step 2 is the important one: this is not a cold-start latency race. It reproduces
on a fully warmed app, on every new session. The gate state is held in
useState(null)with an asyncuseEffectfetch, so each composer mount re-enters theunresolved state regardless of how long the app has been running:
Why this fits the reports:
precedes the async resolution, every time it mounts.
resolved and the helper returns a real boolean.
autoandbypassPermissionsfail together (@ShivSync's observation in #77555) —both run through this same gate, which is exactly what a shared unresolved-config
early-return would produce.
code explicitly tolerates
accessStatus === "blocked_by_org_admin"without failing.A genuine org block does not stop you here. Only the loading state does.
Suggested fix: distinguish "unknown" from "denied". Either have the composer wait on
the already-tracked
configSettled/sessionMetaSettledflags before evaluatingavailability, or make the unresolved case default to the user's configured
defaultModerather than collapsing tofalsevia?? false.Happy to supply the full deobfuscated trace or the surrounding chunk if useful.
Another macOS data point that I think isolates a second, distinct failure mode from the loading race @brianbebel traced above — and which points back at #62205.
On my machine the gate is not racing. It is deterministically
false, and the mode it clamps to is named in a GrowthBook flag value.Environment: macOS 14.6 (Darwin 23.6.0), Claude Desktop 1.24012.9, bundled Claude Code 2.1.220, individual Stripe subscription,
organizationRole: admin(so no enterprise admin above me), nomanaged-settings.jsonon either macOS path.The same account, same settings.json, same machine — CLI works, Desktop does not
I audited every session transcript in one project directory and grouped by
entrypoint:| session |
entrypoint| effectivepermissionMode||---|---|---|
|
2edcfde0|cli| ✅bypassPermissions||
35cd02ad|cli| ✅bypassPermissions||
ae247a1c|cli| ✅bypassPermissions||
c52ddb3c|cli| ✅bypassPermissions||
0a4bacf5|claude-desktop| ❌acceptEdits||
5d80aaec|claude-desktop| ❌acceptEdits||
89e7d90e|claude-desktop| ❌default|~/.claude/settings.jsoncontains"permissions": { "defaultMode": "bypassPermissions" }and"skipDangerousModePermissionPrompt": truethroughout. 100% of CLI sessions honor it; 100% of desktop sessions do not. This is a clean entrypoint split, not a per-session or resume artifact.The clamped mode is literally a flag value
From
~/.claude.jsoncachedGrowthBookFeatures:tengu_quill_harborholds the string"acceptEdits", andacceptEditsis precisely the mode 2 of 3 desktop sessions landed in — despitetengu_disable_bypass_permissions_modebeingfalse, i.e. nothing is supposed to be disabling bypass. That correspondence is hard to read as coincidence, and it matches the mechanism proposed in #62205.The per-account gate is
false, not racingFrom
~/Library/Application Support/Claude/claude_desktop_config.json:Two things to note versus @brianbebel's dump:
bypassPermissionsGateByAccount[<my uuid>]isfalse(theirs istrue). Feedingfalseinto the composer'sb = (x ?? !1) && !vyields a hard deny with no async involvement — so on this machine the outcome is deterministic on every session, warm or cold, rather than a first-render race.bypassPermissionsOptInByAccountdoes not exist at all in my preferences — the key is entirely absent, notfalse.So there appear to be at least two separate paths to the same user-visible symptom: an unresolved-config race (gate
true), and a genuinely-falseaccount gate that seems to be set by experiment assignment rather than by anything the user or an org admin did.The part that actually cost me hours
bypassPermissionsModeEnabledreadstruewhilebypassPermissionsGateByAccount[<uuid>]readsfalse. The UI toggle therefore reports the feature as enabled while the effective gate refuses it, and the session silently downgrades toacceptEditswith no banner, no log line, and no diff between "you asked for bypassPermissions" and "you got acceptEdits". I spent a long debugging session chasing settings.json syntax, managed policies, and session-resume semantics — all of which were fine — because nothing in the product surfaces the real reason.Endorsing @brianbebel's suggested fix and adding two requests:
permissions.defaultModebe silently downgraded. If the resolved mode differs from the configured mode, say so once, in the session, with the reason (config_loading/account_gate/experiment/org_policy/root_detected). Any of those four words would have ended my debugging in seconds.tengu_quill_harbor/tengu_permission_frictioncan clamp a configured mode, an explicitdefaultModein settings.json should take precedence over the experiment arm — or the experiment should be disclosed in/statusor/doctor.Happy to provide the full (redacted) config dump or per-session transcript metadata if it helps. Related: #62205, #73587, #59112.