[BUG] Claude Code persists Bash permission approvals to project .claude/settings.json instead of .claude/settings.local.json

Status Closed — duplicate
Reported on v2.1.218
Maintainer reply None cached
Activity 2 comments · opened Aug 3, 2026 · closed Aug 19, 2026

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?

Bug: Claude Code persists Bash permission approvals to project .claude/settings.json instead of .claude/settings.local.json

Component: Claude Code VSCode extension
Version: 2.1.218 (Claude Code)
Host OS: macOS 15.5 (Build 24F74), Darwin 24.5.0, arm64
Runtime context: Claude Code running as a VSCode native extension (not the standalone CLI). Session operates as an autonomous coding agent making Bash tool calls across multi-step tasks, not a human typing commands interactively at a prompt.

Repository state at time of report:

.claude/settings.json exists, is git-tracked, and is committed/shared across the team (contains a permissions.allow array).
.claude/settings.local.json exists locally in the same repo, is present on disk, and is writable.
Both files conform to the schema at https://json.schemastore.org/claude-code-settings.json.
Expected behavior (per Claude Code settings documentation, https://code.claude.com/docs/en/settings.md and https://code.claude.com/docs/en/permissions.md):
Settings precedence, highest to lowest: managed settings → CLI args → .claude/settings.local.json → .claude/settings.json → user settings (~/.claude/settings.json). Permission approvals persisted during a session (e.g. "yes, don't ask again" for a Bash command) are documented to save to .claude/settings.local.json at the git repo root (resolved through worktrees to the main checkout), specifically because that file is meant for machine-local, non-shared entries.

Actual behavior:
New entries under permissions.allow in .claude/settings.json (the shared, git-tracked file) accumulate across sessions. .claude/settings.local.json is never written to, despite already existing and being writable. This has been observed across multiple distinct sessions on different dates, not a single occurrence.

Confirmed NOT the cause:

Not a stale-version issue: a previously-suspected fix (v2.1.211, addressing a worktree/settings-resolution bug) does not explain this — the behavior persists on 2.1.218, which is newer.
Not a manual-approval artifact: the added entries correspond to Bash commands executed autonomously by the agent during multi-step task execution, not commands a human explicitly ran or clicked "always allow" on in an interactive prompt.
Not permission-mode-specific per documentation: none of the four documented modes (default, plan, acceptEdits, bypassPermissions) are documented to change which settings file receives persisted approvals.
Reproduction steps:

Open a git repository in VSCode with the Claude Code extension installed, where .claude/settings.json exists and is git-tracked, and .claude/settings.local.json also exists.
Start an agent session and have it execute any novel Bash command not already present in permissions.allow (e.g. git ls-remote --heads origin <branch>).
After the tool call completes, inspect .claude/settings.json.
Observe: the new command pattern (e.g. "Bash(git ls-remote *)") has been appended to permissions.allow inside .claude/settings.json, not .claude/settings.local.json.
Example entries observed appended to the shared .claude/settings.json (verbatim, from permissions.allow):

"Bash(git ls-remote *)"
"Bash(echo \"exit: $?\")"
"Bash(git commit -m ' *)"
"Bash(python3 -c ' *)"
"Bash(git -C /Users/<redacted-local-path>/<repo> show <commit-sha> --stat)"
"Bash(timeout 280 tail -F /private/tmp/<redacted-session-path>/tasks/<task-id>.output)"
Note the presence of machine-specific absolute paths and one-off exact-match commands tied to specific, non-recurring identifiers (commit SHAs, ephemeral temp-file task IDs) — these are inherently non-reusable and have no value if shared, which is additional evidence they are being routed to the wrong file (a shared/team file should not accumulate machine-local, one-shot entries).

Impact:
.claude/settings.json is intended as a curated, team-shared permission baseline (committed to version control, reviewed via PR). Because approvals are misrouted to it instead of the local file, every developer's individual sessions continuously dirty this shared file with personal, often non-reusable or path-specific entries, requiring manual periodic cleanup (diffing against last-known-good, stripping drifted entries, and either running git restore .claude/settings.json to discard local drift or opening a dedicated PR to re-baseline the shared file).

Related but distinct issues found on search (same general settings.local.json subsystem, different failure mode — flagging in case they share a root cause):

#15921 — VSCode extension: settings.local.json allow-rules not respected (Bash/Write/Edit still prompt despite config). This is the read-side of the precedence system failing; the present report is about the write-side (new approvals persisting to the wrong file).
#74794 — settings.local.json permissions silently ignored after a project directory rename, due to a trust-dialog/parent-directory interaction. Also read-side, and scoped to a specific rename scenario, not reproduced here.
If these share an underlying cause with the present report (e.g. the extension resolving/writing project-scope settings inconsistently in general), it may be worth investigating together.

Requested outcome: Permission approvals made during VSCode-extension agent sessions should be persisted to .claude/settings.local.json, consistent with documented CLI behavior and the documented settings precedence order, not to the git-tracked .claude/settings.json.

What Should Happen?

Settings precedence, highest to lowest: managed settings → CLI args → .claude/settings.local.json → .claude/settings.json → user settings (~/.claude/settings.json). Permission approvals persisted during a session (e.g. "yes, don't ask again" for a Bash command) are documented to save to .claude/settings.local.json at the git repo root (resolved through worktrees to the main checkout), specifically because that file is meant for machine-local, non-shared entries — see https://code.claude.com/docs/en/settings.md and https://code.claude.com/docs/en/permissions.md.

Expected: when the agent runs a novel Bash command during a VSCode-extension session, the resulting permission approval should be written to .claude/settings.local.json, not to the shared, git-tracked .claude/settings.json.

Error Messages/Logs

Steps to Reproduce

  • Open a git repository in VSCode with the Claude Code extension installed, where .claude/settings.json exists and is git-tracked, and .claude/settings.local.json also exists locally.
  • Start an agent session and have it execute any novel Bash command not already present in permissions.allow (e.g. git ls-remote --heads origin <branch>).
  • After the tool call completes, inspect .claude/settings.json.
  • Observe: the new command pattern (e.g. "Bash(git ls-remote *)") has been appended to permissions.allow inside .claude/settings.json, not .claude/settings.local.json.

Observed repeatedly across multiple distinct sessions on different dates, with commands the agent ran autonomously during multi-step task execution (not commands manually approved via an interactive prompt).

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.218

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

iTerm2

Additional Information

_No response_

View original on GitHub ↗

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