[BUG] permissions from user settings.json is NOT applied at project level

Open 💬 23 comments Opened Aug 5, 2025 by siraj-samsudeen

Environment

  • Platform (select one):
  • Other: Claude Code on macOS with VS Code
  • Claude CLI version: 1.0.68 (Claude Code)
  • Operating System: macOS Darwin 23.6.0
  • Terminal: Terminal App

Bug Description

User-level permissions in ~/.claude/settings.json are not being enforced
during command execution, even though they appear as loaded in the
/permissions command. The same permissions work correctly when placed in
project-level .settings.local.json but fail when moved to user-level
settings.

Steps to Reproduce

  1. Copy working permissions from a project's .settings.local.json to

user-level ~/.claude/settings.json:
{
"permissions": {
"allow": [
"mix:*",
"mcp__serena__*",
"mcp__context7__*",
"Bash(ls:*)",
"Bash(find:*)",
"Bash(grep:*)",
"Bash(sqlite3:*)",
"Bash(mkdir:*)",
"Bash(cp:*)",
"Bash(mv:*)",
"Bash(git add:*)",
"Bash(git push:*)",
"Bash(git checkout:*)",
"Bash(git stash show:*)",
"Bash(git stash pop:*)",
"Bash(git fetch:*)",
"Bash(git stash push:*)",
"Bash(git merge:*)",
"Bash(git cherry-pick:*)",
"Bash(python:*)",
"Bash(python3 -m pip install:*)",
"Bash(npm install:*)",
"Bash(npx playwright test:*)"
]
}
}

  1. Open a project in VS Code with Claude Code
  2. Run /permissions - it correctly shows all permissions as loaded
  3. Try to run any allowed command (e.g., mix --version)
  4. Claude Code still asks for permission despite the command being in the

allow list

Expected Behavior

  • I want a way to allow the mcp of serena to execute all its commands without asking for explicit permission each time.
  • Commands in the user-level allow list should execute without requiring

approval

  • User-level permissions should work identically to project-level

permissions

  • If /permissions shows a command as allowed, it should not require

approval

Actual Behavior

  • All commands require manual approval even when listed in user-level

permissions

  • /permissions command shows the permissions as loaded but they are not

enforced

  • The exact same permissions work when placed in project

.settings.local.json but not in ~/.claude/settings.json

  • MCP wildcard permissions (e.g., mcp__serena__*) also don't work at the

user level

Additional Context

  • These permissions were originally auto-generated by Claude Code (not

manually written) - I just copied them from .settings.local.json to the user-level settings.json

  • This significantly impacts workflow as every command requires manual

approval across all projects

View original on GitHub ↗

23 Comments

github-actions[bot] · 11 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/4365
  2. https://github.com/anthropics/claude-code/issues/1820
  3. https://github.com/anthropics/claude-code/issues/5112

If your issue is a duplicate, please close it and 👍 the existing issue instead.

🤖 Generated with Claude Code

wangkanai · 11 months ago

This permissions issue may be related to #5314 - PostToolUse hooks not triggering for Edit operations on .cs files.

The problem in #5314 involves hooks configured in .claude/settings.json not being executed despite proper syntax and verbose mode being enabled. If permissions from user settings.json are not being applied at the project level as described in this issue, it could explain why PostToolUse hooks fail to execute.

Our hook configuration includes:

{
  "enableAllProjectMcpServers": true,
  "verbose": true,
  "hooks": {
    "PostToolUse": [...]
  }
}

If the settings.json configuration isn't being properly applied at the project level, the hook system might not be receiving the configuration needed to execute hooks after Edit/MultiEdit/Write operations.

Sergiodelahoz · 11 months ago

I'm having the same issue! in MacOS, using claude code, user/.claude/settings.json allow list and deny list commands are completely ignored. It is a bit annoying to deal with this.

tonydehnke · 10 months ago

Same here on MacOS. Would love a real fix.

vhardy · 9 months ago

Same problem on my end. I am trying to run grunt commands from Claude and it keeps asking for permission even though I ask CC to add it to the allow list.

tiagoefreitas · 8 months ago

Same issue, and its extra annoying with plan mode as it does not respect the bypass permissions that I set when launching the cli. The global bypass permissions should be a setting, not a mode. So I tried to workaround by adding permissions and it doesn't use them.

vhardy · 8 months ago

@tiagoefreitas Not sure if this will apply to your situation, but you may want to check /status when in Claude code to see if there were issues loading the settings file. I discovered that it does not proactively report issues with settings files, so it will silently ignore those with typos for example. But if you run /status, then you'll see a message if there was an issue with one of your settings (it was my .claude/settings.json in my case). Then it took a little trial and error to understand where there was a typo because the message did not give details, but I was able to sort it out. I hope this helps.

Sean10 · 8 months ago
@tiagoefreitas Not sure if this will apply to your situation, but you may want to check /status when in Claude code to see if there were issues loading the settings file. I discovered that it does not proactively report issues with settings files, so it will silently ignore those with typos for example. But if you run /status, then you'll see a message if there was an issue with one of your settings (it was my .claude/settings.json in my case). Then it took a little trial and error to understand where there was a typo because the message did not give details, but I was able to sort it out. I hope this helps.

According to your advise, I fixed my problem.

``` bash

check whether settings.json is invalid

/status

claude provide where is invalid

/doctor

arnonmoscona · 8 months ago

Detailed Investigation Report: Permissions Ignored Despite Explicit Configuration

### Environment

  • Claude Code Version: 2.0.34
  • Platform: macOS (Darwin 22.6.0)
  • Project: Local project with .claude/settings.local.json

### Problem Statement
Claude Code repeatedly requests permissions for actions explicitly allowed in .claude/settings.local.json, including:

  • Bash commands: uv run ruff check, uv run ruff format, uv run python -m py_compile
  • MCP tools: mcp__basic-memory__edit_note, mcp__basic-memory__write_note, etc.

All of these permissions were explicitly listed in the permissions.allow array.

### Investigation Steps

#### Theory 1: Path Format Issue (Single vs Double Slash)
Based on issue #6850 and #6881, we hypothesized that absolute paths needed double slashes (//) on macOS.

Changes Made:
```
// Before
"Read(/Users/arnon/projects/**)"
"Bash(/Users/arnon/bin/send_text:*)"

// After
"Read(//Users/arnon/projects/**)"
"Bash(//Users/arnon/bin/send_text:*)"

Result: ❌ Failed - Still requested permissions

Test Method: Fresh Claude instance, asked to run uv run ruff check on a file
Outcome: Permission prompt appeared despite pattern being in allow list

Theory 2: Wildcard Syntax Issue (Colon vs Space)

Based on issue #819, we hypothesized that command:* syntax doesn't match space-separated arguments.

Changes Made:
// Before
"Bash(uv run ruff check:*)"
"Bash(uv run ruff format:*)"

// After
"Bash(uv run ruff *)"

Result: ❌ Failed - Still requested permissions

Test Method: New Claude instance after configuration change
Outcome: Still prompted for permission to run uv run ruff check bin/checked_bash.py

Theory 3: Configuration File Corruption

Rolled back .claude/settings.local.json to a known-good version from Oct 27 (commit a079a14) using git.

Result: ❌ Failed - Still requested permissions with old configuration

Test Method: Fresh Claude instance with rolled-back configuration
Outcome: Permissions still requested, ruling out configuration file as the cause

Evidence of Core Bug

  1. MCP Tool Permissions Ignored:
  • Tool mcp__basic-memory__edit_note explicitly listed on line 40 of settings
  • Claude still prompted: "Do you want to proceed? ... for basic-memory - edit_note commands"
  • No wildcards, no paths, exact string match - still failed
  1. Fresh Instance Behavior:
  • Problem occurs in brand new Claude instances running for <10 seconds
  • Rules out issues from #10028 about periodic clearing after hours of use
  • Indicates permission matching is broken at initialization
  1. Pattern Independence:
  • Failed with single slash paths
  • Failed with double slash paths
  • Failed with colon wildcards
  • Failed with space wildcards
  • Failed with exact MCP tool names (no wildcards at all)

Verification Steps Taken

# Verified settings file exists and contains permissions
$ grep -n "edit_note" .claude/settings.local.json
39: "mcp__basic-memory__edit_note",

# Verified only one settings file in project
$ ls -la .claude/settings*.json
-rw-r--r--@ 1 arnon staff 2845 Nov 6 12:09 .claude/settings.local.json

# Tested with git history
$ git log --oneline -- .claude/settings.local.json
ffe4d62 FLO-72: Add customers table page with sortable columns
99fa209 FLO-72: Implement users table page infrastructure
a079a14 FLO-72: prep for implementing table pages

# Rolled back and tested
$ git checkout a079a14 -- .claude/settings.local.json
# Result: Still prompted for permissions

Screenshot Evidence

Permission prompt appeared for uv run ruff check bin/checked_bash.py despite:

  • Settings containing "Bash(uv run ruff check:)" (later changed to "Bash(uv run ruff )")
  • Fresh Claude instance
  • File explicitly shown in working directory

Conclusion

The permission system in Claude Code 2.0.34 does not read or respect .claude/settings.local.json at all, or the pattern matching is completely broken for all pattern types.

This is not caused by:

  • Path format (single vs double slash)
  • Wildcard syntax (colon vs space)
  • Configuration file corruption
  • Duplicate entries
  • Periodic clearing (happens immediately)

This appears to be a fundamental bug in the permission system initialization or matching logic affecting:

  • All Bash command patterns
  • All MCP tool permissions
  • Both wildcard and exact-match patterns

The system prompts for permissions on every fresh instance regardless of configuration, making the settings file effectively non-functional.

Reproducibility

100% reproducible on every fresh Claude Code instance with any configuration pattern tested.
```

arnonmoscona · 8 months ago

This critical security bug has been open for 3 months with no assignee. Permission system is completely non-functional in v2.0.34. Escalating to
security@anthropic.com and support@anthropic.com

kulesh · 8 months ago
> @tiagoefreitas Not sure if this will apply to your situation, but you may want to check /status when in Claude code to see if there were issues loading the settings file. I discovered that it does not proactively report issues with settings files, so it will silently ignore those with typos for example. But if you run /status, then you'll see a message if there was an issue with one of your settings (it was my .claude/settings.json in my case). Then it took a little trial and error to understand where there was a typo because the message did not give details, but I was able to sort it out. I hope this helps. According to your advise, I fixed my problem. # check whether settings.json is invalid /status # claude provide where is invalid /doctor

This was useful in finding the issues I had in ~/.claude/settings.js Thanks @tiagoefreitas

HansFalkenberg-Visma · 7 months ago

Agree with @arnonmoscona. This bug and its several companions pose a privacy and security risk. Colleagues are advocating the use of --dangerously-skip-permissions because the permission system is so broken. Understandably so, because being prompted for every little detail gets very annoying very fast.

If my ~/.claude/settings.json contains entries in deny, those restrictions had better be respected. If there's a parsing error (as JSON is wont to provoke with its comma requirements), claude MUST abort startup and report such. Rather than the current dangerous behaviour of simply ignoring the settings files and all its denies altogether. This is especially important for the User settings file, which is outside project directories and thus more likely to be edited outside an IDE with live syntax error feedback.

After fixing syntax error and restarting, /permissions lists the expected allows and denies. But claude keeps asking whether it should apply the most trivial of edits. Given that, I'm not particularly trusting that denies are respected either.

deviantintegral · 7 months ago

I ran into this as well, and surprisingly this lead to sandboxing being entirely disabled. It strikes me that sandboxing should be controlled in an entirely separate, simple file so the chances of malformed configuration can never lead to it being disabled.

github-actions[bot] · 6 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

tonydehnke · 6 months ago

This still keeps coming back up. We really could use some attention from the team to fix permissions @bcherny

tiagoefreitas · 6 months ago

between this and claude not reading the user .mcp.json, cc is getting frustrating to use

mrgoonie · 6 months ago

None of these following settings.json works:

With double slashes (at the beginning):

{
  "permissions": {
    "allow": [
      "Write(//private/tmp/test-workspaces/permission-test.txt)"
    ]
  }
}

With a single slash (at the beginning):

{
  "permissions": {
    "allow": [
      "Write(/private/tmp/test-workspaces/permission-test.txt)"
    ]
  }
}

At /private/tmp/test-workspaces/.claude/settings.json and /private/tmp/test-workspaces/.claude/settings.local.json

{
  "permissions": {
    "allow": [
      "Write(*)"
    ]
  }
}

At ~/.claude/settings.json and ~/.claude/settings.local.json:

{
  "permissions": {
    "allow": [
      "Write(*)"
    ]
  }
}

claude was being started at /private/tmp/test-workspaces/

But it always ask for permissions when writing to /private/tmp/test-workspaces/permission-test.txt (although it was pre-approved)

<img width="593" height="381" alt="Image" src="https://github.com/user-attachments/assets/ae0929f4-5fed-45f9-beb7-3a71f778dffa" />

I don't want to run claude with --dangerously-skip-permissions flag. But running claude without it is quite frustrating now...

Wondering how is it working at Anthropic? @bcherny

mrgoonie · 6 months ago

deny is also not working:

/private/tmp/clawk-workspaces/1/.claude/settings.json:

{
  "permissions": {
    "deny": [
      "Write(permission-test.txt)",
      "Write(*)"
    ]
  }
}

Start claude or claude --dangerously-skip-permissions:

<img width="1093" height="1030" alt="Image" src="https://github.com/user-attachments/assets/3673daa7-e98d-48d4-ad51-091563b1adc9" />

claude still write out permission-test.txt

jwhartley · 6 months ago

Solution Found: Path Format Issue

After extensive testing, I found that user-level permissions DO work - the issue is the path format.

Root Cause

Single-slash paths like /Users/james/** are interpreted as relative paths from the settings file location, not absolute paths.

The Fix

Use double-slash // for absolute paths:

{
  "permissions": {
    "allow": [
      "Read(//Users/james/**)",
      "Write(//Users/james/github/**)",
      "Edit(//Users/james/github/**)"
    ]
  }
}

Verification

  1. Updated ~/.claude/settings.json with // paths
  2. Started fresh session (not -r)
  3. All Read/Write/Edit operations work without prompts

This is documented in #6881 but easy to miss. The single-slash format that Claude Code itself generates when you accept prompts may be contributing to the confusion.

Additional Note

Path-restricted Bash commands like Bash(rm:/tmp/**) also don't work reliably. Use wildcards Bash(rm:*) with deny rules instead.

arnonmoscona · 5 months ago

Got tired of waiting for fixes. Created https://github.com/arnonmoscona/toolguard as an enhanced drop-in replacement to the native permissions system using hooks. As far as I can tell it is syntax-compatible, but it has more features and allows also new matching patterns. The simplest setup is just to setup the hook as documented, but you can go far beyond if you want. I will add a new feature soon to overcome some of the annoying behaviors I find around permissions.

I'd welcome any feedback, comments, PRs. This is new, to treat it carefully. "works on my machine" is not really good enough, but I am one guy with one machine. So there's that...

yurukusa · 3 months ago

Workaround — hooks in user settings.json are enforced regardless of project settings:

While permission rules may not cascade correctly from user to project level, hooks defined in ~/.claude/settings.json do apply globally to all projects.

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [
        { "type": "command", "command": "bash ~/.claude/hooks/destructive-guard.sh" },
        { "type": "command", "command": "bash ~/.claude/hooks/branch-guard.sh" }
      ]
    }]
  }
}

Hooks bypass the permission system entirely — they run before tool execution and exit 2 to hard-block, regardless of what project-level settings say.

Quick install: npx cc-safe-setup — writes hooks to ~/.claude/hooks/ and registers them in user-level ~/.claude/settings.json. Works across all projects.

yurukusa · 3 months ago

User-level permissions in ~/.claude/settings.json don't reliably apply at project level — this is a known issue. Hooks are the fix because they always propagate:

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
CLEAN=$(echo "$COMMAND" | sed 's/#.*$//' | tr '\n' ' ')
PARTS=$(echo "$CLEAN" | sed 's/\s*&&\s*/\n/g; s/\s*||\s*/\n/g; s/\s*;\s*/\n/g; s/\s*|\s*/\n/g')
ALL_SAFE=true
while IFS= read -r part; do
    part=$(echo "$part" | sed 's/^\s*//;s/\s*$//')
    [ -z "$part" ] && continue
    BASE=$(echo "$part" | awk '{print $1}')
    case "$BASE" in
        cd|ls|cat|head|tail|grep|rg|find|stat|wc|du|tree|file|which) ;;
        echo|printf|true|false|test|export|env|date|sleep) ;;
        sort|uniq|cut|tr|awk|sed|jq|yq|tee|xargs) ;;
        git|npm|npx|node|yarn|python|python3|pip|make|cargo) ;;
        mkdir|touch|pwd) ;;
        *) ALL_SAFE=false; break ;;
    esac
done <<< "$PARTS"
if [ "$ALL_SAFE" = true ]; then
    jq -n '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"User-level permission (hook)"}}'
fi
exit 0
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/user-level-permissions.sh" }]
    }]
  }
}

Hooks defined in ~/.claude/settings.json fire in every project. Unlike permission patterns, hook execution doesn't have the user/project level scoping bug. This is the reliable way to enforce user-level permissions across all projects.

arnonmoscona · 2 months ago

FYI toolguard is now at 0.3.0 - while takeover mode is nominally in alpha state, it's been working perfectly for me for a while. So this issue does not really affect me any more and I have a much more flexible permissions configuration.

https://github.com/arnonmoscona/toolguard](https://github.com/arnonmoscona/toolguard%5D(https://github.com/arnonmoscona/toolguard))