bypassPermissions: session startup reads flat pref, GUI toggle writes per-account pref — they never sync
Summary
Claude Desktop has two parallel bypass-permissions gates that are never synchronized. The result: the session startup path silently downgrades bypassPermissions to acceptEdits even when the GUI toggle is enabled.
Gates
| Gate | Pref key | What sets it |
|---|---|---|
| GUI toggle | preferences.bypassPermissionsGateByAccount[accountId] | Clicking the bypass toggle in the Desktop UI |
| Session startup check | preferences.bypassPermissionsModeEnabled (flat boolean) | Not exposed in the UI — defaults to false if missing |
Startup path logic (from .vite/build/index.js)
const f = Bi("bypassPermissionsModeEnabled") === true; // reads FLAT pref only
const h = d === sn.Bypass && !f; // true if bypass requested but flat pref is false
const y = h ? sn.AcceptEdits : d; // silently downgrades to AcceptEdits
Bi("bypassPermissionsGateByAccount") is never consulted in this code path. The GUI toggle and the startup-path check are entirely independent.
Symptom
- User enables bypass permissions via the Desktop GUI toggle.
- New session opens — status bar shows
Bypass permissions. - First tool use fires an orange toast: "Bypass Permissions mode isn't enabled."
- Mode silently reverts to
Accept Edits.
Fix (workaround until app-side fix)
Set the flat pref directly in ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"preferences": {
"bypassPermissionsModeEnabled": true
}
}
This must be done while the app is not running (it holds prefs in memory and writes them back on quit, potentially overwriting manual edits).
How this was found
Extracted app.asar with @electron/asar, grepped the bundled JS for the pref name, read the surrounding startup code. Pref names are unobfuscated string literals — greppable directly.
Suggested fix
In the session startup path, check EITHER bypassPermissionsModeEnabled OR bypassPermissionsGateByAccount[accountId] — or better, have the GUI toggle write BOTH prefs atomically so they stay in sync.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗