[Bug] Auto-approve patterns don't match multiline commands (heredocs)

Resolved 💬 38 comments Opened Nov 19, 2025 by michaelgrafwebdev Closed Apr 19, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Description

Auto-approve patterns in settings.json and agent auto_approve_patterns frontmatter fail to match multiline commands containing heredocs or newlines.

Reproduction

  1. Add pattern to settings.json:
"permissions": {
  "allow": [
    "Bash(echo '"
  ]
}
  1. Run a command that starts with that prefix but contains newlines:
echo 'chore: Update files

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>' > /tmp/file.txt
  1. Expected: Command auto-approves (prefix matches)
  2. Actual: Prompts for approval

Patterns Tested (all failed)

  • Bash(echo ' - pure prefix
  • Bash(echo:*) - colon wildcard
  • Bash(echo *) - space wildcard
  • Bash(cat > /tmp/*) - with redirect
  • Agent frontmatter auto_approve_patterns

Impact

  • Cannot auto-approve git commit workflows that use multiline messages
  • Subagents always prompt for heredoc commands
  • Breaks autonomous agent workflows

Environment

  • Claude Code v2.0.30
  • macOS
  • Pattern location: ~/.claude/settings.json and agent frontmatter

Suggested Fix

Either:

  1. Match patterns against first line only (before first newline)
  2. Document that multiline commands cannot be auto-approved
  3. Add a flag for multiline pattern matching

View original on GitHub ↗

38 Comments

zielinski-k-marek · 7 months ago

Same issue as OP. Cannot set allowed on multiline bash command.
Claude Code asks for permission every time and cannot auto approve for session.

If this is by design please document it and provide rationale.

However most useful bash commands for autonomous agent workflows are multiline so essentially the only solution left is --dangerously-skip-permissions flag. In my opinion current behaviour defeats the purpose of tool permissions, because we're back at all or nothing.

Environment

  • Claude Code v2.0.42
  • wsl
fgoni · 7 months ago

How to whitelist this type of commands that are running multiline, or heredoc?
Keeps asking permissions no matter how many allows you try.

tabletenniser · 6 months ago

+1 to this. Also encountered this issue where even though I have Bash(aws:*) listed in my allowed tools, but Claude keeps asking me for approval when it generates a multi-line command like:

aws athena start-query-execution \
  --work-group "primary" \
  --result-configuration 'OutputLocation=s3://aws-athena-query-results...' \
  ......

It says This Bash command contains multiple operations. The following parts require approval: --work-group..., which isn't correct.

sldx · 6 months ago

Hey guys, please fix this bug, it's super annoying. I have to accept _every command_ that the DevBrowser skill is doing to automate/test in the browser.
It makes for a really, really bad experience. Basically, I'm forced to run with _dangerously skip permissions_.

kostyay · 6 months ago

Please fix this bug, super annoying!

github-actions[bot] · 5 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

sldx · 5 months ago

It's still occuring, there's no way for me to pre-approve all sorts of tool calls, I'm forced to run in yolo mode which is not great

zielinski-k-marek · 5 months ago

Still occurring. Super annoying. Forcing to run in yolo mode which is anything but safe.

gsamat · 5 months ago

approving each psql is crazy

Bash( psql default_transaction_read_only *) does not work because all large SQL commands are multiline.

please fix!

aqaurius6666 · 5 months ago

For now, I bypass it by create my safe_script and using hook PermissionRequest to approve any bash has my safe_script

REQUEST=$(cat)

# Extract tool info
TOOL_NAME=$(echo "$REQUEST" | jq -r '.tool_name // empty' 2>/dev/null)
COMMAND=$(echo "$REQUEST" | jq -r '.tool_input.command // empty' 2>/dev/null)

# Log all requests
{
  echo "=== PermissionRequest Event ==="
  echo "Timestamp: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
  echo "Tool: $TOOL_NAME"
  echo "Command: $COMMAND"
  echo ""
} >> "$LOG_FILE"

# Auto-approve Bash commands from skill scripts
if [[ "$TOOL_NAME" == "Bash" ]] && [[ "$COMMAND" =~ \.claude/skills ]]; then
  echo "Decision: ALLOW" >> "$LOG_FILE"
  jq -n '{
    "hookSpecificOutput": {
      "hookEventName": "PermissionRequest",
      "decision": {
        "behavior": "allow"
      }
    }
  }'
  exit 0
else
  echo "Decision: DENY" >> "$LOG_FILE"
  exit 2
fi
raducoriu · 5 months ago

Multiline matching should really be implemented. Claude Code generates commands with bash comments that when you choose to auto allow, only allow the first line of the command i.e. the comment, making auto-approve effectively useless. Allowing all multiline commands that start with comments is also not acceptable.

sldx · 4 months ago

Could someone from @anthropics say if there is any good workaround other than yolo mode?

Maybe some way to allow all and deny some things?

silenvx · 4 months ago

Workaround: Wrote a PermissionRequest hook that normalizes commands and whitelist-matches them. Ended up covering quite a few patterns to handle various multiline cases.

{
  "hooks": {
    "PermissionRequest": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "$HOME/.claude/hooks/approve-safe-commands.py"
      }]
    }]
  }
}

https://gist.github.com/silenvx/5334afee5cd77e2dfcc1be4e943ad43b

Note: This implementation is optimized for my environment. Please use it as a reference and adapt it to your own setup.

amondnet · 4 months ago

You can use Agent-based hooks.

https://x.com/bcherny/status/2017742755737555434

Route permission requests to Opus 4.5 via a hook — let it scan for attacks and auto-approve the safe ones

https://github.com/pleaseai/claude-code-plugins/blob/a718b4773dccb506e15c1a98f47e3467af82d45f/plugins/gatekeeper/hooks/hooks.json#L22

{
  "description": "Gatekeeper: auto-approve safe commands + AI review",
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "node ${CLAUDE_PLUGIN_ROOT}/dist/pre-tool-use.js",
            "timeout": 5
          }
        ]
      }
    ],
    "PermissionRequest": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "agent",
            "prompt": "You are a security analyst. This command was NOT matched by pattern-based rules (Layer 1) and needs your judgment.\n\nCommand context:\n$ARGUMENTS\n\nCheck for these attack patterns:\n1. Data destruction (rm -rf /, truncate, dd if=/dev/zero, etc.)\n2. System modification (chmod 777, chown, modifying /etc/, etc.)\n3. Network attacks (curl piping to bash, wget suspicious scripts, reverse shells)\n4. Credential exposure (cat .env, echo $API_KEY, etc.)\n5. Supply chain attacks (npm install from suspicious sources, pip install --extra-index-url)\n6. Privilege escalation (sudo without clear purpose, setuid)\n7. Command chaining hiding destructive intent — analyze ALL parts of chained commands (;, &&, ||, |, $(), backticks), not just the first\n\nScope guidance:\n- Project-scoped operations (./build, ./dist, node_modules) are generally safe\n- System-scoped operations (/etc, /usr, ~/) require careful scrutiny\n- Standard dev tools (docker run, curl localhost, ssh) are generally safe unless combined with attack patterns above\n\nYou have access to Read, Grep, Glob tools. Use them if:\n- The command references a script file (verify its contents)\n- The command uses variables that might be dangerous\n- You need to check if a path exists and what it contains\n\nAfter your analysis, respond with ONLY one of:\n{\"ok\": true}\n{\"ok\": false, \"reason\": \"Brief explanation of the specific risk\"}",
            "model": "sonnet",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
xhiroga · 4 months ago

I traced the root cause in the bundled source
(node_modules/@anthropic-ai/claude-code/cli.js).

The wildcard matching function converts * to .* and builds a RegExp:

``js
// AS-IS (cli.js, search for: new RegExp(
^${
return new RegExp(^${j}$, K ? "i" : "").test(q);


Without the s (dotAll) flag, . does not match \n, so any multiline command fails.

```js
// TO-BE. works well.
return new RegExp(`^${j}$`, K ? "si" : "s").test(q);
huntson · 4 months ago

+1 — This is disruptive in daily use. The "Command contains a quoted newline followed by a #-prefixed line" prompt fires constantly, especially with git commit heredocs and sub-agents generating multi-line commands. Would love a setting to suppress this.

sldx · 4 months ago

Just so you guys know, I'm afraid I had to switch to Codex and Gemini due to this issue :(

I'm gonna continue keeping my Anthropic Max subscription for a couple of months because I love you guys and I kind of despise OpenAI. Godspeed.

dfaivre-pcs · 4 months ago
Just so you guys know, I'm afraid I had to switch to Codex and Gemini due to this issue :( I'm gonna continue keeping my Anthropic Max subscription for a couple of months because I love you guys and I kind of despise OpenAI. Godspeed.

Agreed. I'm not switching because using /sandbox gets around it, but sandbox itself is pretty buggy. In general, Claude Code seems buggy - but the flow and the potential are just so good. Godspeed +1

amgcc · 4 months ago

Constantly being prompted for innocuous commands like running git diffs or updating PR comments is my biggest issue with claude. Why this is not being fixed as a priority is mind-boggling.

ptdecker · 4 months ago

I will concur the constant prompting for this and "Command contains $() command substituation" is driving me nuts

tjarvstrand · 4 months ago

This is borderline unusable now. Requires constant baby sitting. Well done!

markste-in · 3 months ago

Super disruptive. Other tools handle that better. Fix it pls!

DeanBaron · 3 months ago

This makes claude borderline not autonomous. I need to approve 30 commands for a simple investigation.

yurukusa · 3 months ago

A PreToolUse hook can handle multiline command approval since hooks receive the full command text:

CMD=$(cat | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$CMD" ] && exit 0
FIRST_LINE=$(echo "$CMD" | head -1)
if echo "$FIRST_LINE" | grep -qE '^\s*(echo|cat|git\s+commit)'; then
    jq -n '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"multiline command auto-approved (first line matched)"}}'
fi
exit 0

The key insight: hooks get the complete command including heredoc content, so you can parse the first line separately from the body. The permission system tries to match the entire multiline string against patterns, which fails because .* doesn't cross newlines without the dotAll flag.

BioRodney · 3 months ago

This constant asking for permission for harmless commands makes the tool actually less safe, this is Windows Vista all over again. It just trains everyone to just allow every command. I seriously think that at the end a great amount of people will just blindly select "allow" without even checking anymore what it is asking permission for.

andreality92 · 3 months ago

any news about this? constant babysittings is really annoying, even for simple git commands

ericmatthys · 3 months ago

Not directly related, but https://claude.com/blog/auto-mode helps alleviate this issue indirectly

jakub-bao · 3 months ago

+💯 (plus one hundred!)

I run into this issue on a daily basis. Claude Code is constantly asking me for permissions to issue multi-line commands. That kills longer workflows. I'd initiate a 20-min workflow but I have to check my computer every minute to "allow" Claude Code to execute a multi-line command.

snailwei · 3 months ago

Please fix this, I have to hit YES all day long ,we cannot step away for a cup of coffee.

Command contains newlines that could separate multiple commands
marcobazzani · 3 months ago

this is insane please fix it

jelling · 3 months ago

Triggering constantly with the official Claude code review plugin.

So is the Claude team doing something special when they use the code review plugin? Maybe running with dangerous mode? Because one would assume they would be having this same pain otherwise.

ptdecker · 3 months ago
Triggering constantly with the official Claude code review plugin. So is the Claude team doing something special when they use the code review plugin? Maybe running with dangerous mode? Because one would assume they would be having this same pain otherwise.

I was wondering the very same thing. It's really strange there has been no response to this. Also, it feels like it has been a good amount of time since the last update. Coupled with the downtimes (they are nowhere near production grade uptime levels), it makes me wonder if they are really struggling.

yurukusa · 3 months ago

Hooks can handle multiline commands because they receive the full command string via JSON, not just a pattern-matched prefix.

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
FIRST_LINE=$(echo "$COMMAND" | head -1 | sed 's/^\s*//')
SAFE=false
case "$FIRST_LINE" in
    echo\ *|printf\ *)       SAFE=true ;;
    cat\ \>\ /tmp/*|cat\ \>\>\ /tmp/*) SAFE=true ;;
    git\ commit\ -m\ *)      SAFE=true ;;
    git\ tag\ -a\ *)         SAFE=true ;;
    tee\ /tmp/*)             SAFE=true ;;
esac
if [ "$SAFE" = true ]; then
    jq -n '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"First line matches safe multiline pattern"}}'
fi
exit 0

The most common multiline scenario is git commit -m "multi\nline\nmessage":

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
FIRST_LINE=$(echo "$COMMAND" | head -1 | sed 's/^\s*//')
if echo "$FIRST_LINE" | grep -qE '^git\s+commit\s'; then
    jq -n '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"git commit auto-approved"}}'
fi
exit 0
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/heredoc-allow.sh" }]
    }]
  }
}

The hook receives the full command via jq -r '.tool_input.command' — newlines, heredocs, everything. Pattern matching in settings.json operates on the serialized string where newlines break matching. The hook can extract just the first line, apply your prefix logic, and auto-approve regardless of what follows.
For the agent frontmatter auto_approve_patterns case: hooks set in ~/.claude/settings.json propagate to subagents, so the same hook handles agent-spawned heredocs too.

bcherny collaborator · 3 months ago

The original bug here — * in Bash(...) allow rules not matching across newlines — was fixed in early March 2026 (the wildcard regex now uses the dotAll flag). The cross-repo "Fixes" link didn't auto-close this issue, so it stayed open.

Several follow-on comments in this thread are separate issues, not the original newline bug:

  • prompts on $() / $VAR substitution → tracked separately, fix in progress
  • backslash line-continuation being treated as "multiple operations" → separate command-splitting issue
  • "quoted newline followed by #-prefixed line" heuristic → separate guard

If you're still hitting the original "wildcard doesn't match multiline command" behavior on a current version, please open a fresh issue with the exact rule + command. Otherwise we'll close this one out.

raducoriu · 3 months ago

@bcherny Just changing the regex matching from one line to multi-line doesn't sound like a proper fix. Do you have a link with the actual fix? What happens when there are actually multiple commands and not just comments?

michaelgrafwebdev · 3 months ago

if it were a proper fix, would we have 50 people complaining about the same
thing and still tired of the constant responding to prompts that Boris is
obviously contending with daily, or are the Anthropic team all running
dangerous permissions? some of us are dropping $200/mo hoping to not have
to run in dangerous mode....

On Wed, Apr 8, 2026 at 1:34 AM Radu Coriu @.***> wrote:

raducoriu left a comment (anthropics/claude-code#11932) <https://github.com/anthropics/claude-code/issues/11932#issuecomment-4204914442> @bcherny <https://github.com/bcherny> Just changing the regex matching from one line to multi-line doesn't sound like a proper fix. Do you have a link with the actual fix? What happens when there are actually multiple commands and not just comments? — Reply to this email directly, view it on GitHub <https://github.com/anthropics/claude-code/issues/11932#issuecomment-4204914442>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/ALISRFER3UZO52QXUYN5QZD4UYFJ7AVCNFSM6AAAAACMRSC57KVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DEMBUHEYTINBUGI> . You are receiving this because you authored the thread.Message ID: @.***>

--
Michael Graf
Web Developer

🌐 www.customwebsitesforyou.com
📧 @.***
🤖 AI Secretary: 916‑252‑1313 <19162521313>

claude[bot] contributor · 2 months ago

This issue was fixed as of version 2.1.72.

github-actions[bot] · 2 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.