Settings Parser Fails on Complex Commands with Embedded Parentheses
Summary
The settings file permission parser fails when processing Bash commands with embedded parentheses (e.g., PowerShell commands with COM object method calls), displaying a validation error: "Empty parentheses. Either specify a pattern or use just 'Bash' without parentheses." This prevents Claude Code from starting and requires manual editing of the settings file.
Environment
- Claude Code Version: 2.0.72
- Platform: Linux (WSL2)
- OS Version:
Linux 6.6.87.2-microsoft-standard-WSL2 - Shell: bash
- Settings File:
<project>/.claude/settings.local.json
Detailed Description
Trigger Event
User had previously approved PowerShell commands that included complex COM object method calls. These commands were stored in .claude/settings.local.json as permission patterns. When Claude Code attempted to load the settings file on startup, the parser encountered embedded parentheses in the function calls and failed validation.
Error Message
Settings Error
/home/cyber/projects/.claude/settings.local.json
└ permissions
└ allow
└ "Bash(powershell.exe -Command '
$shell = New-Object -ComObject Shell.Application
$fenix = $shell.NameSpace(17).Items() | Where-Object { $_.Name -like ""*fenix*"" }
...
└ Empty parentheses. Either specify a pattern or use just "Bash" without parentheses.
Problematic Permission Entries
Lines 80-81 in settings file (before fix):
"Bash(powershell.exe -Command '\n$shell = New-Object -ComObject Shell.Application\n$fenix = $shell.NameSpace(17).Items() | Where-Object { $_.Name -like \"\"*fenix*\"\" }\n...')",
"Bash(powershell.exe -Command '\n$shell = New-Object -ComObject Shell.Application\n...\n$dest.CopyHere($file, 16)\n...')"
The parser encountered:
$shell.NameSpace(17).Items()- Method calls with parenthesesWhere-Object { $_.Name -like ... }- Lambda blocks with parentheses$dest.CopyHere($file, 16)- Method calls with arguments
Parser Behavior
The permission parser uses a pattern Bash(pattern) where pattern is expected to be a wildcard or command prefix. It fails when:
- The pattern contains
()(empty parentheses) - interpreted as invalid syntax - The pattern contains method calls like
.Items()or.CopyHere(arg, arg)- parser sees the embedded()and rejects
Impact Assessment
User Impact: Critical
- Prevents Claude Code from starting - User must select "Exit and fix manually"
- No graceful degradation - Cannot skip just the invalid entries
- Entire settings file skipped - "Files with errors are skipped entirely, not just the invalid settings"
- Requires manual JSON editing to recover
Frequency: Low
- Only affects users who:
- Run complex PowerShell commands with method calls
- Use "Don't ask again" to persist those commands
- Are on WSL/Linux systems executing Windows PowerShell
Data Loss Risk: None
- Settings file remains intact, just rejected by parser
- User can manually edit to recover
Reproduction Steps
Minimal Reproduction
- Create or edit
<project>/.claude/settings.local.jsonwith this content:
{
"permissions": {
"allow": [
"Bash(powershell.exe -Command '$shell.Method()')"
]
},
"outputStyle": "default"
}
- Start Claude Code
Expected Result:
- Claude Code starts normally
- Permission pattern is loaded
Actual Result:
- Settings validation error: "Empty parentheses. Either specify a pattern or use just 'Bash' without parentheses"
- User forced to choose "Exit and fix manually" or "Continue without these settings"
Real-World Reproduction
-
Approve a Bash command containing a PowerShell script with COM object method calls:
$shell = New-Object -ComObject Shell.Application $items = $shell.NameSpace(17).Items() -
Select "Don't ask again" when prompted for permission
-
Exit Claude Code
-
Restart Claude Code
Expected Result:
- Claude Code starts normally
- Permission is preserved and applied
Actual Result:
- Claude Code displays settings validation error
- User forced to choose "Exit and fix manually" or "Continue without these settings"
- All permissions in the file are skipped
Root Cause Analysis
The permission pattern parser appears to use a simple regex or string matching approach that:
- Expects
Bash(pattern)format - Interprets any
()within the pattern as potentially invalid - Does not account for:
- Escaped characters in bash commands
- Multiline commands with
\n - Programming language syntax (PowerShell, Python) within bash strings
- Method calls, function invocations, or lambda expressions
Why This Fails
The PowerShell command is wrapped as:
Bash(powershell.exe -Command 'SCRIPT WITH PARENTHESES')
The parser sees .NameSpace(17) and .Items() and interprets the embedded () as malformed permission syntax rather than part of the command string.
Workarounds
Immediate Fix (Manual Edit)
Replace the specific long PowerShell commands with a wildcard pattern:
"Bash(powershell.exe:*)"
This allows all PowerShell commands but loses granularity.
Preventive Workaround
When using complex commands:
- Avoid "Don't ask again" for commands with method calls or complex syntax
- Use wildcard patterns instead:
Bash(powershell.exe:*),Bash(python3:*)
Suggested Fixes
Option 1: Improve Parser Escaping (Recommended)
Treat the content inside Bash(...) as a literal string, not a pattern with special syntax:
- Only parse the outer
Bash(and)delimiters - Everything between is the command pattern (can contain any characters)
- Escape handling should preserve parentheses in strings
Option 2: Validation Before Saving
Before writing permissions to settings file:
- Validate that the command can be re-parsed successfully
- If validation fails, fall back to wildcard pattern
- Warn user that specific command was simplified
Option 3: Alternative Format for Complex Commands
For commands with special characters, use an alternate encoding:
{
"pattern": "Bash",
"command": "powershell.exe -Command '...'"
}
This separates the pattern from the full command and avoids parser ambiguity.
Related Issues
Potentially Related:
- #5710 - "[BUG] "Don't Ask Again" Overwrites Entire Permissions Array"
- Settings corruption during permission updates
- May be related mechanism that caused these commands to be persisted
Debug Log Evidence
Session: ca033e2f-1c45-46dc-9ca5-299378395dff
Debug Log: ~/.claude/debug/ca033e2f-1c45-46dc-9ca5-299378395dff.txt
Settings load failure (Line ~6):
2025-12-18T02:13:09.837Z [DEBUG] Applying permission update: Adding 15 allow rule(s) to destination 'userSettings': ...
2025-12-18T02:13:09.837Z [DEBUG] Applying permission update: Adding 91 allow rule(s) to destination 'localSettings': ...
Notice that localSettings has 91 rules loaded successfully after manual fix, indicating the file structure is otherwise valid.
Files for Reference
- Settings File (Before Fix): Contains problematic PowerShell commands on lines 80-81
- Settings File (After Fix): Replaced with
"Bash(powershell.exe:*)" - Debug Log:
~/.claude/debug/ca033e2f-1c45-46dc-9ca5-299378395dff.txt(58 KB)
Test Cases for Validation
The parser should successfully handle these permission patterns:
[
"Bash(ls:*)",
"Bash(python3:*)",
"Bash(node -e 'console.log()')",
"Bash(bash -c 'echo $(date)')",
"Bash(powershell.exe -Command '$shell.Method()')",
"Bash(python3 -c 'import sys; print(sys.version)')"
]
Currently FAILS on:
Bash(powershell.exe -Command '$shell.Method()')- Method calls with ()- Any command containing
.NameSpace(),.Items(),.GetFolder(), etc.
Next Steps
- [ ] File GitHub issue with reproduction steps
- [ ] Attach example settings file excerpt (sanitized)
- [ ] Propose parser fix (Option 1 recommended)
- [ ] Add test cases for complex command patterns