[BUG] Bash permission glob pattern fails to match paths containing parentheses (Next.js route groups)
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?
The Bash(cat:*) permission pattern fails to match cat commands when the file path contains parentheses, such as Next.js App Router route groups like (private) and (public). Despite selecting "Yes, and don't ask again for: cat:*" multiple times, the user is re-prompted every time.
The permission IS correctly saved to settings.local.json and settings.json — the issue is that the glob matcher doesn't match the command at runtime.
What Should Happen?
Bash(cat:*) should match cat "/path/to/app/(private)/layout.tsx" since * should match any character sequence including parentheses.
Steps to Reproduce
- Have a Next.js App Router project with route groups (directories named
(private),(public), etc.) - Have
Bash(cat:*)in your permissions allow list (both~/.claude/settings.jsonand.claude/settings.local.json) - Claude Code (or an MCP tool) runs:
cat "/path/to/project/apps/web/src/app/(private)/layout.tsx" - Expected: Command auto-allowed (matches
cat:*) - Actual: User prompted to approve, with options including "Yes, and don't ask again for: cat:*" — which they've already selected before
Why This Matters
Next.js App Router uses parenthesized directory names as a core feature (route groups). Common patterns:
app/(private)/layout.tsx— authenticated routesapp/(public)/layout.tsx— public routesapp/(auth)/login/page.tsx— auth-related routesapp/(marketing)/page.tsx— marketing pages
This means every Next.js App Router project with route groups will hit this bug for any Bash command that touches files in those directories.
Permission State (verified)
~/.claude/settings.json:
{
"permissions": {
"allow": [
"Bash(cat:*)"
]
}
}
<project>/.claude/settings.local.json:
{
"permissions": {
"allow": [
"Bash(cat:*)"
]
}
}
Both files contain the pattern. The pattern is saved but never matches.
Prompt Shown to User
Bash command
cat "/Users/.../apps/web/src/app/(private)/layout.tsx"
Read private layout
This command requires approval
Do you want to proceed?
❯ 1. Yes
2. Yes, and don't ask again for: cat:*
3. No
Error Messages/Logs
No error — the permission just silently fails to match.
Related Issues
- #14522 — Parentheses in PowerShell commands inside
Bash()pattern (parser-level failure, different root cause) - #26630 — Special characters in quoted paths (Windows, closed as dup of #15292)
- #15292 — Env var prefixes not matching (closed/stale)
- #34379 —
#character breaks matching
These are all closed and cover different specific cases. This issue is specifically about parentheses in file paths (not in the command syntax), which is especially impactful for the Next.js ecosystem.
Claude Model
Opus
Is this a regression?
No, this never worked
Claude Code Version
Latest (March 2026)
Platform
Anthropic API
Operating System
macOS (Darwin 24.6.0)
Terminal/Shell
zsh
Additional Information
Workaround: Use the dedicated Read tool instead of cat via Bash — this bypasses the Bash permission system entirely. However, MCP tools and subagents may still invoke cat, causing the prompt to appear.
Suggested fix: The glob matcher for Bash() permission patterns should treat * as matching any character (including parentheses, brackets, etc.) — or use simple string prefix matching rather than glob matching for the :* suffix pattern.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗