[Critical] Claude Code Glob patterns (/**) in settings.local.json don't work - breaks developer workflow

Status Fixed / completed
Maintainer reply ✓ Yes — bogini
Activity 14 comments · opened Aug 30, 2025 · closed Aug 19, 2026
💡 Likely answer: A maintainer (bogini, collaborator) responded on this thread — see the highlighted reply below.

Environment

  • Platform (select one):
  • [x] Other: Claude Code in PyCharm IDE
  • Claude CLI version:  1.0.98 (Claude Code)
  • Operating System: macOS
  • Terminal: PyCharm IDE

## Bug Description
The /** glob pattern in .claude/settings.local.json permissions is not granting recursive access to subdirectories and files as expected. Despite
having a wildcard permission for a parent directory, Claude continues to request explicit permission for individual files within that directory tree.

## Steps to Reproduce

  1. Add permission to .claude/settings.local.json: "Read(/Users/sam/repos/parcelping/nozzles/**)"
  2. Ask Claude to read a file deep in the directory structure, e.g., /Users/sam/repos/parcelping/nozzles/src/core/config.py
  3. Claude requests permission for that specific file despite the parent directory having /** permission
  4. Must manually approve each individual file access request

## Expected Behavior
When granting permission with glob pattern "Read(/path/to/repo/**):

  • Should allow reading ALL files in /path/to/repo/
  • Should allow reading ALL subdirectories recursively
  • Should allow reading ALL files in ALL subdirectories recursively
  • Should follow standard glob pattern conventions where /** means "everything recursively"

## Actual Behavior

  • Claude requests permission for each individual file
  • Parent directory permission with /** is ignored
  • Must add redundant permissions for every subdirectory:

```json
"Read(/Users/sam/repos/parcelping/nozzles/)",
"Read(/Users/sam/repos/parcelping/nozzles/src/
)",
"Read(/Users/sam/repos/parcelping/nozzles/src/core/)",
"Read(/Users/sam/repos/parcelping/nozzles/docs/
)",

  • Settings file becomes bloated with hundreds of redundant entries

Additional Context

Impact on Development Workflow:

  • Constant interruptions to grant permissions breaks flow
  • Settings file becomes unmaintainable with hundreds of entries
  • Defeats the purpose of wildcard patterns
  • Contradicts standard glob behavior used in gitignore, rsync, find, etc.

Current Workaround:
Must either:

  1. Grant very broad permissions like "Read(/Users/sam/repos/**)"
  2. Manually add every subdirectory path (defeats purpose of wildcards)
  3. Constantly approve individual file access requests during sessions

Severity: High - Significantly impacts developer experience and makes the permissions system unnecessarily cumbersome for real-world development
scenarios where access to entire repository trees is needed.

View original on GitHub ↗

13 Comments

github-actions[bot] · 1 year ago

Found 1 possible duplicate issue:

  1. https://github.com/anthropics/claude-code/issues/6850

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

tonydehnke · 1 year ago

Wonder if this is causing my issue too. Have you found a workaround or other pattern that works as it seems like this bug keeps coming back to Claude often.

bogini collaborator · 12 months ago

Hi @Latigid-Sam, Thank you for the detailed bug report. After reviewing your issue and our documentation, I believe the problem is with the path format in your permission rules. See https://docs.anthropic.com/en/docs/claude-code/iam#tool-specific-permission-rules.

"Read(/Users/sam/repos/parcelping/nozzles/**)" is being interpreted as a relative path from your settings file location, not as an absolute filesystem path. So Claude Code is looking for {settings-directory}/Users/sam/repos/... which doesn't exist.

The following should work:

Use double slashes // for absolute filesystem paths:
"Read(//Users/sam/repos/parcelping/nozzles/**)"

You could also use:

  • "Read(~/repos/parcelping/nozzles/**)" if repos is in your home directory
  • "Read(//Users/sam/repos/**)" for broader access to all repositories

This should resolve the issue where Claude Code repeatedly asks for permission despite having the glob pattern. The /** pattern itself is correct for recursive access - it was just looking in the wrong location.

Please try updating your settings.local.json with the double-slash format and let us know if this resolves the issue!

tonydehnke · 12 months ago
Hi @Latigid-Sam, Thank you for the detailed bug report. After reviewing your issue and our documentation, I believe the problem is with the path format in your permission rules. See https://docs.anthropic.com/en/docs/claude-code/iam#tool-specific-permission-rules. "Read(/Users/sam/repos/parcelping/nozzles/**)" is being interpreted as a relative path from your settings file location, not as an absolute filesystem path. So Claude Code is looking for {settings-directory}/Users/sam/repos/... which doesn't exist. The following should work: `` Use double slashes // for absolute filesystem paths: "Read(//Users/sam/repos/parcelping/nozzles/**)" ` You could also use: * "Read(~/repos/parcelping/nozzles/)" if repos is in your home directory * "Read(//Users/sam/repos/)" for broader access to all repositories This should resolve the issue where Claude Code repeatedly asks for permission despite having the glob pattern. The /**` pattern itself is correct for recursive access - it was just looking in the wrong location. Please try updating your settings.local.json with the double-slash format and let us know if this resolves the issue!

The problem is, it's Claude Code that is suggesting and adding the invalid paths, if this suggestion is the fix.

So Claude Code still needs to be fixed.

github-actions[bot] · 8 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.

TimHolahan · 8 months ago

I created bug issue #13629 but will allow it to remain closed since I believe it's a duplicate of this one.

The double-slash solution proposed above did not resolve the problem for me. Using "dangerously skip permissions" mode prevents the bug from occurring, as you might expect, but that isn't a real solution.

Since this problem is so fundamental to CC tool-use and permissions, I'd hope it might get some developer attention. I'd be happy to test solutions or provide more information.

jwhartley · 7 months ago

Confirming This Solution Works

Just wanted to confirm @bogini's solution completely fixed my permission issues.

What Worked

Changed from:

"Read(/Users/james/**)"

To:

"Read(//Users/james/**)"

Both user-level (~/.claude/settings.json) and project-level (settings.local.json) work correctly with this format.

Additional Finding: Bash Path Patterns Don't Work

While testing, I also discovered that path-restricted Bash patterns don't work:

"Bash(rm:/tmp/**)"   // Does NOT match "rm /tmp/file.txt"
"Bash(rm:*)"         // Works - use this with deny rules instead

The // fix only applies to Read/Write/Edit tools. For Bash commands, use wildcards and rely on deny rules for safety.

Suggestion

This double-slash requirement should be prominently documented, and Claude Code should generate the correct format when users accept permission prompts.

jodidac · 7 months ago

## Root Cause Identified - Technical Investigation

@bogini Thanks for investigating this! I've completed a detailed technical analysis that shows this isn't a path format issue—the root cause is an undocumented bypass mechanism that completely skips the permission checking system.

### TL;DR

Root Cause: Fast-path bypass at cli.js:2342 (function Rl5) skips entire GL → Es → fC → Vr2 permission chain. Pre-checker computing bypass parameter has bug handling root !== "/" scenarios.

Evidence: ES6 instrumentation proves ZERO executions of GL/Es/fC/Vr2
Fix: 3-line code change (tested)
Timeline: ~2-3 hours with source access

---

### Why Path Format Theory Doesn't Explain This

The suggestion to use //Users/... vs /Users/... would only matter if the permission checking code (fC/GL) was executing. Instrumentation proves it never runs:

Function Call Counts (for Write operations):
✓ [STARTUP]: 1
❌ [GL]: 0 ← Permission checker NEVER CALLED
❌ [Es]: 0 ← NEVER CALLED
❌ [fC]: 0 ← NEVER CALLED (where path conversion happens)
❌ [Vr2]: 0 ← NEVER CALLED

The entire documented permission system is bypassed.

---

Bypass Mechanism Discovered

Location: cli.js:2342 (function Rl5)

```javascript
function Rl5(A,B) {
return dP2.useCallback(async(Q,I,G,Z,Y,J) => {
// When J !== undefined, GL is completely skipped
return (J !== void 0 ? Promise.resolve(J) : GL(Q,I,G,Z,Y))
.then(...)
}, [A,B]);
}


When parameter J (pre-computed permission) is provided, the entire GL chain never executes.

---
Pre-Checker Bug (Root Cause)

A fast-path pre-checker computes J before Rl5. This pre-checker has a bug similar to the one found in fC (line 2004):

Current code (BUGGY):

```javascript
  // Only handles root === "/" case
  if (root === "/") {
    if (pattern.startsWith("/")) {
      pattern = pattern.slice(1);
    }
  }
  // Missing: Case for root !== "/"

The Fix (3 lines):

  if (root !== null && root !== "/" && pattern.startsWith(root)) {
    pattern = pattern.slice(root.length);
    if (pattern.startsWith("/")) pattern = pattern.slice(1);
  }

---
Investigation Deliverables

Complete package available with:

  • Instrumentation proof (zero-execution logs)
  • Automated reproduction test
  • Deobfuscated permission code
  • Tested fix
  • Architecture analysis

---
Questions for Team

  1. Can you confirm pre-checker location in unminified source?
  2. Why maintain dual permission systems (GL path + fast-path)?
  3. Does pre-checker use ignore library like fC?

This explains why this issue has been open since August—the bypass means standard debugging won't find it. Hope this helps unblock the fix!

jodidac · 7 months ago

## Root Cause Identified - Technical Investigation

@bogini @jamesbraza Thanks for investigating! I can confirm the // workaround works,
but I've identified why: it's accidentally avoiding a deeper bug, not fixing it.

### TL;DR

Root Cause: Fast-path bypass at cli.js:2342 (function Rl5) skips entire
permission chain. Pre-checker has bug handling root !== "/" - the // workaround
accidentally avoids triggering this bug.

Evidence: ES6 instrumentation proves permission code never executes
Fix: 3-line code change (tested) - will make // workaround unnecessary

---

### Why the // Workaround Works (But Shouldn't Be Needed)

The //Users/... workaround succeeds because it accidentally bypasses buggy
root-handling logic:

```javascript
// Pre-checker logic (simplified):
if (root !== "/" && pattern.startsWith(root)) {
// Convert absolute → relative (BUGGY for root !== "/")
pattern = pattern.slice(root.length);
}


  Normal pattern: "/Users/james/**"
  → Triggers conversion → Bug causes failure

  Workaround: "//Users/james/**"
  → Doesn't startsWith(root) → Skips conversion → Works by accident


  Problems with this workaround:
  - ❌ Undocumented behavior
  - ❌ Breaks Bash patterns (as @jamesbraza noted)
  - ❌ Counter-intuitive (Unix paths don't use //)
  - ❌ Fragile (normalization could break it)
  - ❌ Doesn't fix underlying bug

  ---
  Instrumentation Evidence: Permission Code Never Runs

  Created ES6-compatible instrumentation to verify the bypass:

  Function Call Counts (Write operations):
    ✓ [STARTUP]: 1
    ❌ [GL]:      0   ← Permission checker NEVER CALLED
    ❌ [Es]:      0   ← NEVER CALLED
    ❌ [fC]:      0   ← NEVER CALLED (documented path conversion)
    ❌ [Vr2]:     0   ← NEVER CALLED

  The documented permission system (GL → Es → fC → Vr2) is completely bypassed.

  ---
  Bypass Mechanism Discovered

  Location: cli.js:2342 (function Rl5)

```javascript
  function Rl5(A,B) {
    return dP2.useCallback(async(Q,I,G,Z,Y,J) => {
      // When J !== undefined, entire GL chain is skipped
      return (J !== void 0 ? Promise.resolve(J) : GL(Q,I,G,Z,Y))
        .then(...)
    }, [A,B]);
  }

A fast-path pre-checker computes J before Rl5. This pre-checker has the bug that
the // workaround avoids.

---
The Actual Bug (Found in fC, Likely Replicated in Pre-Checker)

Location: cli.js:2004 (function fC)

// Current code (BUGGY):

if (root === "/") {
    if (pattern.startsWith("/")) {
      pattern = pattern.slice(1);
    }
  }
  // Missing: Case for root !== "/" (e.g., /Users/james/project)

The Fix (3 lines):

  if (root !== null && root !== "/" && pattern.startsWith(root)) {
    pattern = pattern.slice(root.length);
    if (pattern.startsWith("/")) pattern = pattern.slice(1);
  }

This fix eliminates the need for the // workaround.

---
Why This Matters

For Users:

  • No more cryptic // requirement
  • Bash patterns will work correctly: Bash(rm:/tmp/**)
  • Standard Unix path conventions work as expected

For Anthropic:

  • Explains why this issue has been open 5+ months
  • Shows why standard debugging won't find it (bypass mechanism)
  • Provides ready-to-apply fix (2-3 hours with source access)

---
Investigation Package Available

Complete documentation includes:

  • Instrumentation proof (zero-execution logs)
  • Automated reproduction test
  • Deobfuscated permission code
  • Tested fix
  • Architecture analysis
jfrome1 · 7 months ago

Confirming this affects Windows (Claude Code v2.1.5)

Environment: Windows 11, Claude Code v2.1.5 (terminal)

Reproduction:

  1. Global ~/.claude/settings.json has 70+ permissions configured (e.g., Bash(cp:*))
  2. Started fresh session
  3. Ran cp --version
  4. Still prompted for permission despite allow-list entry

Also tested: Manually added Bash(cp:*) to ~/.claude.json under projects."C:/Dev/repos/AI-projects".allowedTools - also not enforced.

Additional observation: The "Yes, and always allow similar commands in this directory" UI option doesn't persist - the allowedTools array remains empty after selection.

Symptoms match the root cause analysis in #6881 (permission checking code bypassed entirely). Adding Windows to the affected platforms.

nikhilsitaram · 6 months ago

Still need fix on this.

relipse · 4 months ago

This also did not work the last 2 days.

OliPetry · 3 months ago

Hitting this same issue on macOS (Darwin 25.4.0), Claude Code 2.1.133. Filed #57746 independently before finding this thread.

Root cause hypothesis: If the permission matcher uses Python's pathlib.PurePath.match() (or a JS equivalent with the same semantics), ** silently fails to match more than one directory level deep:

from pathlib import PurePath
path = PurePath('/Users/me/.config/project/worktrees/branch/scripts/file.py')
path.match('/Users/me/.config/project/**')           # False (broken)
path.match('/Users/me/.config/project/*/*/*/*/*')     # True

fnmatch.fnmatch() or any glob library with proper ** recursion handles this correctly. If the codebase is Go or Node, the same class of bug exists in filepath.Match (Go, no ** support) and path.matchesGlob (Node <20, no ** support).

This has been open since v1.0.98 and affects every user who tries to allowlist a directory tree. Would love to see it prioritized.

Showing cached comments. Read the full discussion on GitHub ↗