[BUG] Multi-line bash permission patterns with heredocs corrupt settings.json
[BUG] Multi-line bash permission patterns with heredocs corrupt settings.json
Description
When multi-line bash commands containing heredoc operators are added to Claude Code's permission system, they corrupt settings.json with "Unmatched ' in Bash pattern" errors, completely blocking all Claude Code operations.
Environment
- Claude Code Version: 2.0.54+
- OS: macOS (also affects other platforms)
- Project Type: All
Problem Statement
Symptoms:
Settings Error
/path/to/.claude/settings.local.json
└ permissions
└ allow
└ "Bash(...multi-line heredoc...)" : Unmatched ' in Bash pattern.
Ensure all quotes are properly paired
Files with errors are skipped entirely, not just the invalid settings.
Root Cause:
Multi-line bash commands (691 chars, 24 newlines) with heredoc operators bypass validation and get added to settings.json, causing JSON parse errors.
Steps to Reproduce
- Have a working Claude Code installation with
.claude/settings.local.json - Attempt to add a multi-line bash pattern with heredoc
- This pattern gets added to settings.json (via permission prompt or direct edit)
- Claude Code fails to start with "Unmatched ' in Bash pattern" error
- All Claude Code operations blocked until manual JSON editing
Expected Behavior
Multi-line commands with heredocs should be rejected during validation with a clear error message explaining why they're invalid.
Actual Behavior
Multi-line heredoc patterns are accepted and corrupt settings.json, blocking all operations.
Impact
- Severity: Critical - Blocks entire Claude Code session
- User Impact: Complete loss of access until manual JSON repair
- Workaround: Manual JSON editing required (non-trivial for non-developers)
- Frequency: Rare but catastrophic when it occurs
Malformed Entry Characteristics
The problematic entry was:
- 691 characters (should reject >500 chars)
- 24 newlines (multi-line command)
- Heredoc operator (
<<) - Starts with comment (
#) - Shell keywords (try, tell, etc.)
All of these should trigger validation rejection but currently don't.
Validation Gap
Claude Code currently lacks comprehensive validation for permission patterns. Needed checks include:
- ✅ Parentheses balancing (currently checked)
- ✅ Command substitution
$(...)(currently checked) - ❌ Newlines/multi-line commands (NOT checked)
- ❌ Heredoc operators
<<,<<<(NOT checked) - ❌ Shell constructs
if,for,while(NOT checked) - ❌ Comment patterns starting with
#(NOT checked) - ❌ Length limits >500 chars (NOT checked)
- ❌ Quote balancing single+double (NOT checked)
Suggested Fix
Add comprehensive permission pattern validation before writing to settings.json:
def validate_permission_pattern(pattern: str) -> dict:
"""Validate permission pattern before adding to settings."""
# Check for newlines (multi-line commands)
if '\\n' in pattern or '\\r' in pattern:
return {"valid": False, "reason": "Multi-line commands not allowed"}
# Check for heredoc operators
if '<<' in pattern:
return {"valid": False, "reason": "Heredoc operators not allowed"}
# Check for length
if len(pattern) > 500:
return {"valid": False, "reason": f"Pattern too long ({len(pattern)} chars, max 500)"}
# Check for shell constructs
if re.search(r'\\b(if|for|while|do|then|else|fi)\\b', pattern):
return {"valid": False, "reason": "Shell constructs not allowed"}
# Check for comments
if pattern.strip().startswith('Bash(#'):
return {"valid": False, "reason": "Comment patterns not allowed"}
# Existing checks...
return {"valid": True}
Related Issues
- #5710 - "Don't Ask Again" Overwrites Entire Permissions Array (different but related)
- #7300 - Claude Code writes broken Permission Syntax (different pattern type)
Additional Context
User Workarounds:
chmod 444 .claude/settings.local.json(prevents corruption but breaks permission additions)- Manual JSON editing (requires technical knowledge)
- Git checkout to restore (if settings are version-controlled)
Detection:
This issue may be related to how permission patterns are captured from bash commands. If Claude is executing multi-line heredocs and then trying to save the entire command as a permission pattern, the pattern extraction logic needs to be enhanced to reject such patterns upfront.
Reproduction Rate
- Low frequency (requires specific multi-line bash commands)
- High impact (complete session block when it occurs)
- 100% reproducible with malformed patterns
---
Note: This issue has been observed in the wild and required creating custom PreToolUse hooks to prevent settings.json corruption. The validation logic described in "Suggested Fix" has been tested against 600+ existing permission patterns with zero false positives.
This issue has 8 comments on GitHub. Read the full discussion on GitHub ↗