Claude Code incorrectly saves bash script fragments as permission patterns
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?
When a user approves execution of a multi-line bash script, Claude Code incorrectly parses and saves individual script lines (variable assignments, conditionals, loop constructs) as separate permission patterns in settings.local.json. These invalid patterns then cause settings parsing errors on subsequent launches.
Environment:
Claude Code 2.1.14
OS: Ubuntu 24.04
The permission-saving logic appears to be splitting multi-line bash input on newlines and treating each line as a separate command to whitelist, rather than recognizing that the entire block is a single script execution.
Additionally, bash parameter expansion syntax (${var%%pattern}, ${var##pattern}) contains : characters that conflict with the permission pattern syntax (:* for prefix matching), causing parse errors.
Workaround:
Manually edit ~/.claude/settings.local.json to remove invalid entries, or delete the file entirely to regenerate defaults:
bashmv ~/.claude/settings.local.json ~/.claude/settings.local.json.bak
What Should Happen?
Only the top-level command or script invocation should be saved as a permission pattern, not individual lines within a multi-line script. For example, if a user approves a script that starts with for os in "${OS_LIST[@]}", the permission saved should be something like Bash(for os in:*) at most — or ideally nothing, since loop constructs aren't meaningful permission patterns.
Error Messages/Logs
Individual script lines are saved as permission patterns:
json{
"permissions": {
"allow": [
"Bash(dir=\"$os%%:*\")",
"Bash(name=\"$os##*:\")",
"Bash(if [[ -f \"/srv/tftp/ppc/$dir/vmlinux\" ]])",
"Bash([[ -f \"/srv/tftp/ppc/$dir/initrd.gz\")",
"Bash(then)",
"Bash(else)",
"Bash(fi)",
"Bash(bios_ok=false)",
"Bash(for:*)",
"Bash(do)",
"Bash(done)"
]
}
}
On next launch, Claude Code fails to parse settings:
Settings Error
/home/user/.claude/settings.local.json
└ permissions
└ allow
└ "Bash(dir="$os%%:*")": The :* pattern must be at the end. Move :* to the end
for prefix matching, or use * for wildcard matching.
Files with errors are skipped entirely, not just the invalid settings.
Steps to Reproduce
Use Claude Code to write and execute a multi-line bash script containing variable assignments with parameter expansion, conditionals, or loop constructs
Approve the script execution when prompted
Examine ~/.claude/settings.local.json
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
2.1.14
Claude Code Version
2.1.14
Platform
Anthropic API
Operating System
Other Linux
Terminal/Shell
Other
Additional Information
Suggested Fixes:
Do not save individual lines from multi-line script blocks as separate permissions
Validate permission patterns before saving to settings file
Escape or handle : characters that appear in bash syntax to avoid conflicts with the :* pattern suffix
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗