[BUG] Claude Code generates malformed permission patterns in settings.local.json, causing validation errors and possible terminal crashes
Resolved 💬 3 comments Opened Dec 31, 2025 by Yi-Yi-y Closed Jan 3, 2026
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?
Claude Code is automatically generating incorrectly-formatted permission patterns when saving bash
command permissions to settings.local.json. The patterns use instead of : for prefix matching, causing validation errors on every startup. Additionally, the terminal crashes intermittently, particularly when certain commands (WebFetch, file operations) are executed, which may be related to the settings validation failing.
What Should Happen?
- Permission patterns should use : for prefix matching: "Bash(Speaker:)"
- Complex piped commands should be saved as single permission entries
- Settings should validate successfully
- Terminal should not crash
Error Messages/Logs
Example Malformed Entries
"Bash(Speaker*)",
"Bash(Transcript*)",
"Bash(Audio*)",
"Bash(powershell -Command \"Get-Process | Where-Object {$_ProcessName -like ''*whisper*''} | Select-Object ProcessName, Id, Path\")",
"Bash(Where-Object {$_DisplayName -like \"*audio*\" -or $_Name -like \"*audio*\"})",
"Bash(Format-Table -AutoSize Name, DisplayName, Status, StartType)"
Settings Error
C:\Users\Robean\.claude\settings.local.json
└ permissions
└ allow
├ "Bash(Speaker*)": Use ":*" for prefix matching, not just "*". Change to "Bash(Speaker:*)"
for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(Transcript*)": Use ":*" for prefix matching, not just "*". Change to
"Bash(Transcript:*)" for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(Audio*)": Use ":*" for prefix matching, not just "*". Change to "Bash(Audio:*)" for
prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(powershell -Command "Get-Process | Where-Object {$_ProcessName -like ''*whisper*''} |
Select-Object ProcessName, Id, Path")": Use ":*" for prefix matching, not just "*". Change to
"Bash(powershell -Command "Get-Process | Where-Object {$_ProcessName -like '':*whisper:*''} |
Select-Object ProcessName, Id, Path")" for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(Where-Object {$_DisplayName -like "*audio*" -or $_Name -like "*audio*"})": Use ":*"
for prefix matching, not just "*". Change to "Bash(Where-Object {$_DisplayName -like ":*audio:*"
-or $_Name -like ":*audio:*"})" for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
└ "Bash(powershell
├ exe -Command "Get-Service | Where-Object {$_
───────────────────────────────────────────────────────────────────────────────────────────────────── Settings Error
C:\Users\Robean\.claude\settings.local.json
└ permissions
└ allow
├ "Bash(Speaker*)": Use ":*" for prefix matching, not just "*". Change to "Bash(Speaker:*)"
for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(Transcript*)": Use ":*" for prefix matching, not just "*". Change to
"Bash(Transcript:*)" for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(Audio*)": Use ":*" for prefix matching, not just "*". Change to "Bash(Audio:*)" for
prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(powershell -Command "Get-Process | Where-Object {$_ProcessName -like ''*whisper*''} |
Select-Object ProcessName, Id, Path")": Use ":*" for prefix matching, not just "*". Change to
"Bash(powershell -Command "Get-Process | Where-Object {$_ProcessName -like '':*whisper:*''} |
Select-Object ProcessName, Id, Path")" for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(Where-Object {$_DisplayName -like "*audio*" -or $_Name -like "*audio*"})": Use ":*"
for prefix matching, not just "*". Change to "Bash(Where-Object {$_DisplayName -like ":*audio:*"
-or $_Name -like ":*audio:*"})" for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
└ "Bash(powershell
───────────────────────────────────────────────────────────────────────────────────────────────────── Settings Error
C:\Users\Robean\.claude\settings.local.json
└ permissions
└ allow
├ "Bash(Speaker*)": Use ":*" for prefix matching, not just "*". Change to "Bash(Speaker:*)"
for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(Transcript*)": Use ":*" for prefix matching, not just "*". Change to
"Bash(Transcript:*)" for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(Audio*)": Use ":*" for prefix matching, not just "*". Change to "Bash(Audio:*)" for
prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(powershell -Command "Get-Process | Where-Object {$_ProcessName -like ''*whisper*''} |
Select-Object ProcessName, Id, Path")": Use ":*" for prefix matching, not just "*". Change to
"Bash(powershell -Command "Get-Process | Where-Object {$_ProcessName -like '':*whisper:*''} |
Select-Object ProcessName, Id, Path")" for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
├ "Bash(Where-Object {$_DisplayName -like "*audio*" -or $_Name -like "*audio*"})": Use ":*"
for prefix matching, not just "*". Change to "Bash(Where-Object {$_DisplayName -like ":*audio:*"
-or $_Name -like ":*audio:*"})" for prefix matching. Examples: Bash(npm run:*), Bash(git:*)
└ "Bash(powershell
├ exe -Command "Get-Service | Where-Object {$_
│ └ DisplayName -like ''*audio*'' -or $_
│ └ Name -like ''*audio*''} | Format-Table -AutoSize Name, DisplayName, Status,
StartType")": Use ":*" for prefix matching, not just "*". Change to "Bash(powershell.exe -Command
"Get-Service | Where-Object {$_.DisplayName -like '':*audio:*'' -or $_.Name -like '':*audio:*''} |
Format-Table -AutoSize Name, DisplayName, Status, StartType")" for prefix matching. Examples:
Bash(npm run:*), Bash(git:*)
└ exe -Command "Get-CimInstance Win32_PnPSignedDriver | Where-Object {$_DeviceClass -eq
''MEDIA'' -or $_DeviceName -like ''*audio*''} | Select-Object
DeviceName,DriverVersion,DriverDate,Manufacturer | Format-List")": Use ":*" for prefix matching,
not just "*". Change to "Bash(powershell.exe -Command "Get-CimInstance Win32_PnPSignedDriver |
Where-Object {$_DeviceClass -eq ''MEDIA'' -or $_DeviceName -like '':*audio:*''} | Select-Object
DeviceName,DriverVersion,DriverDate,Manufacturer | Format-List")" for prefix matching. Examples:
Bash(npm run:*), Bash(git:*)
Files with errors are skipped entirely, not just the invalid settings.
Steps to Reproduce
- Use Claude Code and allow it to run bash commands (particularly PowerShell commands with wildcards or piped commands)
- When prompted for permission, approve and allow Claude Code to save the permission
- Check ~/.claude/settings.local.json - malformed patterns appear like:
- "Bash(Speaker)" instead of "Bash(Speaker:)"
- "Bash(Transcript)" instead of "Bash(Transcript:)"
- Fragmented pipeline commands saved as separate entries
- Settings validation errors appear on startup
- Terminal crashes randomly, especially during WebFetch or certain tool operations
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.075.
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
VS Code integrated terminal
Additional Information
Additional Context
- User never manually edits settings file - all entries are auto-generated by Claude Code
- Issue persists across multiple sessions
- Malformed entries appear to be generated when Claude runs commands on user's behalf
- PowerShell commands with wildcards inside (e.g., -like 'audio') seem particularly problematic
- Terminal crashes are infrequent but correlate with certain command executions (WebFetch, file operations)
Impact
- Settings validation errors on every startup
- Cannot use wildcards for bash command permissions without manual editing
- Potential terminal instability/crashes
- User experience degradation
Even when the settings file has been validated again, and fixed by claude code, it returns within a few days.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗