Project-scope permission grant strips unknown top-level keys from .claude/settings.json
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?
When Claude Code writes a new permission rule to project-level .claude/settings.json (triggered by approving an "Always allow (project)" permission prompt), it appears to drop top-level keys it doesn't recognize. In my case, the extraKnownMarketplaces and enabledPlugins blocks were silently deleted while a new permissions.allow entry was being added.
This is destructive — those keys are part of the documented schema (used to register plugin marketplaces and enable plugins via bootstrap scripts), and losing them silently disables plugins for everyone who pulls the next commit.
Secondary issue in the same write: the new permission entries embed the user's absolute home-directory path, which doesn't generalize to other contributors when the file is committed.
What Should Happen?
- A new entry is added to
permissions.allow. - All other top-level keys (
extraKnownMarketplaces,enabledPlugins, and any other valid keys) are preserved verbatim. - Permission rules generated from "Always allow (project)" do not contain absolute user-home paths; either the paths are normalized, or "Always allow" for home-dir paths writes to
settings.local.json/ user scope instead of project scope.
Error Messages/Logs
diff --git a/.claude/settings.json b/.claude/settings.json
@@ -7,18 +7,9 @@
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git push *)",
- "Bash(npx some-bootstrap-cli *)"
+ "Bash(npx some-bootstrap-cli *)",
+ "Bash(ls \"C:\\\\Users\\\\<user>\\\\.claude\\\\projects\\\\<project-slug>\\\\memory\\\\\" 2>&1 | head -30)",
+ "Read(//c/Users/<user>/.claude/projects/<project-slug>/**)"
]
- },
- "extraKnownMarketplaces": {
- "example-marketplace": {
- "source": {
- "source": "github",
- "repo": "example-org/example-marketplace-repo"
- }
- }
- },
- "enabledPlugins": {
- "example-plugin@example-marketplace": true
}
}
Steps to Reproduce
- In any project, create
.claude/settings.jsonwith at least one top-level key besidespermissions. For example:
{
"permissions": { "allow": ["Bash(npm view *)"] },
"extraKnownMarketplaces": {
"example-marketplace": { "source": { "source": "github", "repo": "example-org/example-marketplace-repo" } }
},
"enabledPlugins": { "example-plugin@example-marketplace": true }
}
- Open Claude Code in that project.
- Run any command that will trigger a permission prompt (e.g.
ls some/new/path). - On the prompt, pick "Always allow (project)" — the option that writes to project-level
.claude/settings.json. - Inspect
.claude/settings.json.
Observed: the new permission entry is added under permissions.allow, but extraKnownMarketplaces and enabledPlugins are deleted entirely. The permission entries that get written also embed the user's absolute home-directory path verbatim.
Expected: the new permission entry is added and the other top-level keys are preserved untouched.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.165
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
VS Code integrated terminal
Additional Information
Secondary observation (possibly a separate issue):
The auto-written permission entries embedded the absolute Windows path including my username (C:\Users\<user>\.claude\projects\<project-slug>\...). When .claude/settings.json is checked into version control (the documented purpose of project-scope settings, alongside settings.local.json for personal overrides), these paths leak the local user's home directory into the shared team file and don't generalize to other contributors.
Possible mitigations:
- Strip or normalize user-home paths before writing to project scope.
- OR: default "Always allow" writes for paths under the user's home directory to
settings.local.jsoninstead of project scope.
Workaround in the meantime:
Pick "Always allow (local)" or "Always allow (user)" rather than "Always allow (project)" — those land in settings.local.json (gitignored) or ~/.claude/settings.json respectively and don't touch the team-shared file.
Impact:
- Silent regression of plugin/marketplace configuration whenever any team member triggers a project-scope permission grant. The next
git pullpropagates the deletion to everyone. - User-specific absolute paths leak into team-shared settings via permission rules — bad for both portability and incidentally exposes Windows usernames in committed files.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗