Permission glob matching: absolute path (/Users/...) fails when Claude Code normalizes to ~ internally

Resolved 💬 3 comments Opened Mar 26, 2026 by HengWoo Closed Mar 30, 2026

Summary

Permissions set with absolute paths in settings.json silently fail to match when Claude Code normalizes paths to ~ form internally before comparison.

Environment

  • Claude Code version: (latest as of 2026-03-26)
  • OS: macOS
  • Permission entry location: ~/.claude/settings.json (global user settings)

Steps to Reproduce

  1. Add a Write permission using an absolute path in ~/.claude/settings.json:

``json
"permissions": {
"allow": [
"Write(/Users/heng/.claude/users/**)"
]
}
``

  1. Trigger a Claude Code session (particularly a remote trigger / scheduled agent / heartbeat) that attempts to write to a path like /Users/heng/.claude/users/ou_xxx/daily/2026-03-26.md
  2. Claude Code prompts for permission despite the glob pattern being set

Verification

Node.js path.matchesGlob('/Users/heng/.claude/users/ou_xxx/daily/2026-03-26.md', '/Users/heng/.claude/users/**') returns true — the pattern is syntactically correct and matches the target path.

Root Cause (Suspected)

Claude Code appears to normalize permission patterns (or target paths) to ~-prefixed form before comparison. If the stored permission uses an absolute path (/Users/heng/...) but the comparison is done against ~/.claude/..., the strings don't match and the permission check falls through to a prompt.

Fix / Workaround

Adding both the ~ form and the absolute path form resolves the issue:

"permissions": {
  "allow": [
    "Write(/Users/heng/.claude/users/**)",
    "Write(~/.claude/users/**)"
  ]
}

After adding the ~-prefixed variants, the next write to the same path succeeded without a prompt.

Expected Behavior

Write(/Users/heng/.claude/users/**) and Write(~/.claude/users/**) should be treated as equivalent since they resolve to the same path. The permission check should normalize both to a canonical form before comparison — not match only one form.

Impact

This is particularly problematic for scheduled agents / remote triggers (e.g. heartbeat tasks), because:

  • They run in a potentially different process context
  • The user is not present to approve the prompt interactively
  • The permission was intentionally set to allow automated writes, but silently fails

Users who only use interactive sessions may not encounter this because they can approve the one-time prompt — which Claude Code then remembers. Automated/headless contexts have no such fallback.

View original on GitHub ↗

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