claude-code icon indicating copy to clipboard operation
claude-code copied to clipboard

Settings Parser Fails on Complex Commands with Embedded Parentheses

Open pentafive opened this issue 1 week ago • 2 comments

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 parentheses
  • Where-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:

  1. The pattern contains () (empty parentheses) - interpreted as invalid syntax
  2. 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

  1. Create or edit <project>/.claude/settings.local.json with this content:
{
  "permissions": {
    "allow": [
      "Bash(powershell.exe -Command '$shell.Method()')"
    ]
  },
  "outputStyle": "default"
}
  1. 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

  1. Approve a Bash command containing a PowerShell script with COM object method calls:

    $shell = New-Object -ComObject Shell.Application
    $items = $shell.NameSpace(17).Items()
    
  2. Select "Don't ask again" when prompted for permission

  3. Exit Claude Code

  4. 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:

  1. Expects Bash(pattern) format
  2. Interprets any () within the pattern as potentially invalid
  3. 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

  1. Settings File (Before Fix): Contains problematic PowerShell commands on lines 80-81
  2. Settings File (After Fix): Replaced with "Bash(powershell.exe:*)"
  3. 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

pentafive avatar Dec 18 '25 19:12 pentafive