[BUG] Multi-line bash commands are fragmented when saved to settings.local.json

Resolved 💬 3 comments Opened Feb 1, 2026 by jflavigne-sidlee Closed Feb 5, 2026

[BUG] Multi-line bash commands are fragmented when saved to settings.local.json

Description

Claude Code CLI incorrectly fragments multi-line bash commands when saving them to .claude/settings.local.json during runtime. This results in invalid permission entries that cannot be parsed correctly.

Environment

  • Claude Code Version: 2.1.16
  • OS: macOS (Darwin 25.1.0)
  • Installation Method: native
  • Observed in: Multiple projects (agents-sdk, user-story-agent-dev, dev-agent)

Steps to Reproduce

  1. Start Claude Code CLI in a project directory
  2. Run a multi-line bash command, for example:

``bash
for f in docs/work/tickets/*/; do echo "=== $f ==="; done
``

  1. Check .claude/settings.local.json after the session
  2. Observe fragmented entries in the permissions.allow array

Expected Behavior

The permission should be saved as a single entry that represents the complete command pattern, such as:

{
  "permissions": {
    "allow": [
      "Bash(for *)"
    ]
  }
}

Or the multi-line command should be normalized to a pattern that makes sense as a permission entry.

Actual Behavior

The multi-line command is incorrectly split into multiple invalid entries:

{
  "permissions": {
    "allow": [
      "Bash(for dir in /Users/jflavigne/agents-sdk/docs/work/tickets/*/)",
      "Bash(done)",
      "Bash(while read f)",
      "Bash({} ;)"
    ]
  }
}

Evidence

From /Users/jflavigne/agents-sdk/.claude/settings.local.json (166 lines total):

{
  "permissions": {
    "allow": [
      // ... other valid entries ...
      "Bash(done)",                    // Line 32 - Fragment from loop
      "Bash({} ;)",                    // Line 68 - Fragment
      "Bash(while read f)",            // Line 108 - Incomplete loop
      "Bash(for dir in /path/to/tickets/*/)",  // Line 151 - Loop start
      // ... more entries ...
    ]
  }
}

These entries trigger validation warnings:

Agent Parse Errors
└ Failed to parse 6 settings entries

Impact

High Severity - This bug causes:

  1. Immediate user-facing errors: "6 invalid settings" warnings appear when opening Claude Code
  2. Persistent corruption: Since settings.local.json is treated as PROTECTED during updates, corrupted entries persist indefinitely
  3. Propagation across projects: When using deployment scripts that copy .claude/settings.local.json, the corruption spreads to new projects
  4. Doctor command noise: Users see persistent validation warnings that cannot be easily resolved

Root Cause Analysis

Based on investigation:

  1. Claude Code modifies .claude/settings.local.json at runtime to add permission entries for executed commands
  2. When processing multi-line bash commands (loops, conditionals, complex pipes), the parser appears to split on shell keywords (for, do, done, while, etc.) or delimiters
  3. Each fragment is saved as a separate permission entry
  4. These fragments are syntactically invalid as standalone bash patterns

Propagation Chain

Work in project → Run multi-line bash → Fragmented entries saved
                                                ↓
                                    settings.local.json corrupted
                                                ↓
                        Use project as deployment SOURCE
                                                ↓
                        deploy-agent.sh copies corrupted file
                                                ↓
                            Target project now corrupted
                                                ↓
                        Updates skip settings.local.json (PROTECTED)
                                                ↓
                                Corruption persists forever

Proposed Fix

Option 1: Don't save multi-line commands to permissions

  • Detect multi-line bash commands (contain \n, ;, &&, etc.)
  • Skip adding them to settings.local.json

Option 2: Normalize to patterns

  • Extract the base command(s) and save as wildcards
  • Example: for f in *; do cat $f; done["Bash(for *)", "Bash(cat *)"]

Option 3: Save complete command as single entry

  • Escape/encode the full multi-line command
  • Ensure it's valid JSON

Workaround

For users experiencing this:

  1. Manually clean .claude/settings.local.json:

``bash
# Remove malformed entries
# Keep only valid patterns like "Bash(command *)"
``

  1. Before deploying dev-agent to new projects, reset the source's settings.local.json:

``bash
echo '{"permissions": {"allow": []}}' > .claude/settings.local.json
``

Additional Context

This bug was discovered while investigating recurring "6 invalid settings" warnings across multiple projects. The investigation revealed that:

  • Fresh compose output generates clean settings.local.json
  • During runtime, Claude Code appends permission entries
  • Multi-line commands get fragmented
  • Deployment scripts copy the corrupted file to new projects
  • PROTECTED file status prevents automatic fixes during updates

Related Files

  • .claude/settings.local.json - Affected file
  • scripts/deploy-agent.sh - Propagates corruption via copy
  • Claude Code CLI runtime - Source of fragmentation

---

Reproduction Rate: 100% when running multi-line bash commands

User Impact: All users who:

  • Run multi-line bash commands in Claude Code sessions
  • Deploy dev-agent using active development directories as source
  • Use worktree workflows with shared configurations

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗