[BUG] No write-time validation for permission patterns causes settings corruption
What's Wrong?
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
Bug Description
When Claude Code prompts for permission on Bash commands containing environment variables (e.g., SSH_AUTH_SOCK=...), clicking "Always allow" saves invalid patterns to the settings file.
The saved patterns use incorrect syntax that Claude Code itself rejects on the next startup, causing the entire settings file to be skipped.
The Core Problem
Write path has no validation, but read path does.
- Claude runs a command like:
SSH_AUTH_SOCK=/tmp/ssh-XXXXXX/agent.1234 ssh-add -l - User clicks "Always allow"
- Claude Code saves:
"Bash(SSH_AUTH_SOCK=*)"← Invalid syntax - On next startup:
⚠ Found invalid settings files. They will be ignored. /doctorshows:
````
Invalid Settings .claude/settings.local.json
└ permissions
└ allow
└ "Bash(SSH_AUTH_SOCK=*)": Use ":*" for prefix matching, not just "*".
Change to "Bash(SSH_AUTH_SOCK=:*)" for prefix matching.
Why This Is Critical
- Silent data loss: All other valid settings in the file are also ignored (in my case 70+ carefully configured allow rules)
- Recurring issue: User keeps hitting "Always allow" → keeps corrupting the file
- No warning at write time: User has no idea the pattern is invalid until next session
- Requires manual intervention: Must hand-edit JSON to fix
- Overly specific patterns: "Always allow" also saves useless session-specific patterns like full temp paths
Real-World Examples from My Settings
These patterns were automatically generated by clicking "Always allow":
Invalid Patterns (cause settings to be skipped entirely)
"Bash(SSH_AUTH_SOCK=*)"
Error: Use ":*" for prefix matching, not just "*". Change to "Bash(SSH_AUTH_SOCK=:*)"
Useless Over-Specific Patterns (saved but will never match again)
"Bash(SSH_AUTH_SOCK=/tmp/ssh-XXXXXXf0Vl4w/agent.9840 ssh-add:*)"
"Bash(SSH_AUTH_SOCK=/tmp/ssh-JqzBQXKuG1AX/agent.9840 ssh-add:*)"
"Bash(SSH_AUTH_SOCK=/tmp/ssh-JqzBQXKuG1AX/agent.9840 ssh:*)"
"Bash(SSH_AUTH_SOCK=$SSH_AUTH_SOCK ssh:*)"
These contain session-specific temp paths that change every time. They will never match future commands.
Giant Commit Message Patterns (absurd)
"Always allow" even saved entire git commit messages as patterns:
"Bash(git commit -m \"$(cat <<'EOF'\nAdd comprehensive caching layer...\n[500+ characters of commit message]\nEOF\n)\")"
This is clearly not a reusable pattern.
Related Issues
- #8581 - Permission wildcards not working for env vars (closed as duplicate)
- #5886 - Settings validation passes invalid types (open, related validation issue)
- #2593 - Config corruption from concurrent sessions (different root cause)
- #11172 - "Always allow" not persisting (possibly related)
Workaround
I've written a bash script that fixes these patterns before starting Claude Code. Happy to share if others need it.
What Should Happen?
What Should Happen
For Invalid Syntax
Option A (Preferred): Validate patterns before saving. If the pattern would be invalid:
- Auto-correct it (
*→:*for env var prefixes) - Or show an error and don't save
- Or ask the user to confirm a corrected version
Option B: Make the read-path more tolerant:
- Warn about invalid patterns but don't skip the entire file
- Ignore only the invalid entries, keep the rest
For Over-Specific Patterns
When a command contains:
- Temp paths (
/tmp/ssh-XXXXX/...) - Variable expansions (
$SSH_AUTH_SOCK) - Multi-line content (heredocs, commit messages)
Claude Code should either:
- Generalize the pattern (e.g.,
SSH_AUTH_SOCK=/tmp/... ssh-add→Bash(SSH_AUTH_SOCK=:*)) - Warn the user that this pattern is too specific to be useful
- Not save it at all and suggest adding a general pattern manually
Error Messages/Logs
Example patterns saved by "Always allow" that cause settings corruption:
INVALID PATTERNS (cause entire settings file to be skipped):
"Bash(SSH_AUTH_SOCK=*)"
USELESS PATTERNS (session-specific, will never match again):
"Bash(SSH_AUTH_SOCK=/tmp/ssh-XXXXXX/agent.1234 ssh-add:*)"
"Bash(SSH_AUTH_SOCK=/tmp/ssh-YYYYYY/agent.5678 ssh-add:*)"
"Bash(SSH_AUTH_SOCK=$SSH_AUTH_SOCK ssh:*)"
ABSURD PATTERNS (entire commit messages saved as "patterns"):
"Bash(git commit -m \"$(cat <<'EOF'\n[500+ character commit message]\nEOF\n)\")"
---
/doctor output on next startup:
Invalid Settings .claude/settings.local.json
└ permissions
└ allow
└ "Bash(SSH_AUTH_SOCK=*)": Use ":*" for prefix matching, not just "*".
Change to "Bash(SSH_AUTH_SOCK=:*)" for prefix matching.
---
Startup warning:
⚠ Found invalid settings files. They will be ignored. Run /doctor for details.
---
Result: All 70+ valid permission entries in the file are also ignored.
Steps to Reproduce
Steps to Reproduce
- Start Claude Code in any project
- Ensure SSH agent is running with a socket (common on Linux/WSL)
- Ask Claude to run:
ssh-add -lor any SSH command - Claude will prepend
SSH_AUTH_SOCK=...to the command - When prompted, click "Always allow"
- Exit Claude Code
- Restart Claude Code
- Observe:
⚠ Found invalid settings files. They will be ignored. - All your other permissions are now also ignored
Environment
- Claude Code Version: Latest (issue persists across versions)
- Platform: Linux (WSL2 Ubuntu 24.04)
- Terminal: zsh
- Shell: zsh with ssh-agent running
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.0.75 (Claude Code)
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
WSL (Windows Subsystem for Linux)
Additional Information
_No response_
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗