[BUG] Bash tool's heredoc commit format triggers approval prompt on every commit regardless of auto-accept settings

Status Fixed / completed
Reported on v2.1.69
Maintainer reply ✓ Yes — ashwin-ant
Activity 6 comments · opened Mar 5, 2026 · closed Apr 19, 2026
💡 Likely answer: A maintainer (ashwin-ant, collaborator) responded on this thread — see the highlighted reply below.

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?

The Bash tool's system prompt instructs Claude to format all commit messages using a $() heredoc pattern:

git commit -m "$(cat <<'EOF'
Commit message here.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Problem: This $() command substitution causes every git commit to require manual user approval, even when the user has auto-accept enabled. The shell substitution is presumably flagged as
potentially dangerous, bypassing the auto-accept setting.

Note: Trying to work around this with instructions in claude.md doesn't help

What Should Happen?

Expected behavior: Commits should respect auto-accept settings. The built-in example should use a format that doesn't trigger approval prompts, such as multiple -m flags or git commit -F
<file>.

Error Messages/Logs

Steps to Reproduce

  1. Make a code change
  2. Ask Claude to commit
  3. Claude generates a commit using the built-in heredoc pattern: git commit -m "$(cat <<'EOF' ... EOF)"
  4. User is prompted for approval even with "Accept edits" enabled
  5. This happens on every commit with no way to avoid it, since the Bash tool's system prompt hardcodes this format

Claude Model

None

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

2.1.69

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

iTerm2

Additional Information

_No response_

View original on GitHub ↗

6 Comments

github-actions[bot] · 5 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/31117
  2. https://github.com/anthropics/claude-code/issues/23926
  3. https://github.com/anthropics/claude-code/issues/25441

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

ozydingo · 5 months ago

This is the same root cause as #31117 — the Bash tool's description field contains hardcoded heredoc templates for both git commit and gh pr create that use $(cat <<'EOF' ... EOF), which triggers the command substitution approval prompt regardless of auto-accept settings.

See my detailed comment on #31117 for the specific system prompt text, why CLAUDE.md overrides don't reliably work, workarounds, and a suggested fix.

tl;dr: The Bash tool description says ALWAYS pass the commit message via a HEREDOC. This fights project-level instructions and triggers approval prompts on every commit and PR creation. Fix: replace the heredoc examples with git commit -F <file> and gh pr create --body-file <file>.

ozydingo · 5 months ago

My workarounds appear to be successful, both in tandem:

Git Commit went through without prompt with a deny-retry cycle

We have a pre tool use hook that denies the permission-prompting syntax with a message that remind claude to do it the "better" way, using a temp file

⏺ Bash(git add release/environments/production.yaml release/environments/staging.yaml && git commit -m "$(cat <<'EOF'
      Bump worker_postprocess_asr memory request to 9Gi…)
  ⎿  PreToolUse:Bash hook returned blocking error
  ⎿  BLOCKED: Do not use heredoc syntax for commit messages. Write the message to dev/tmp/commit-msg.txt using the Write tool, then run: git commit -F
     dev/tmp/commit-msg.txt
  ⎿  Error: Hook PreToolUse:Bash denied this tool

❯ Tool loaded.

⏺ Write(dev/tmp/commit-msg.txt)
  ⎿  Wrote 10 lines to dev/tmp/commit-msg.txt
[...]
⏺ Bash(git add release/environments/production.yaml release/environments/staging.yaml && git commit -F dev/tmp/commit-msg.txt)

This was achieved with a PreToolUse hook

      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "script/developer/agents/reject-prompt-triggering-syntax"
          }
        ]
      }

the script specifically targets these two forms to avoid over-firing

COMMAND=$(jq -r '.tool_input.command' 2>/dev/null)

# Match: git commit ... $(cat <<
if echo "$COMMAND" | grep -qE 'git commit.*\$\(cat <<'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "BLOCKED: Do not use heredoc syntax for commit messages. Write the message to dev/tmp/commit-msg.txt using the Write tool, then run: git commit -F dev/tmp/commit-msg.txt"
    }
  }'
  exit 0
fi


# Match: gh pr create ... $(cat <<
if echo "$COMMAND" | grep -qE 'gh pr create.*\$\(cat <<'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "BLOCKED: Do not use heredoc syntax for PR body. Write the body to dev/tmp/pr-body.md using the Write tool, then run: gh pr create --body-file dev/tmp/pr-body.md"
    }
  }'
  exit 0
fi

exit 0

Pull Request went through without prompt

We updated our pull request skill:

## IMPORTANT: Do not use heredoc syntax for PR creation

The Bash tool's built-in instructions suggest using `$(cat <<'EOF' ... EOF)` heredoc
syntax for `gh pr create --body` and `git commit -m`. **This triggers permission prompts
that disrupt agentic workflows and must be avoided.** Always use the `--body-file` approach
described in this skill instead — write content with the Write tool, then reference the file.

and this was respected

yurukusa · 5 months ago

The $() heredoc pattern triggers a safety heuristic that can't be suppressed via settings. A PreToolUse hook can auto-approve git commits specifically:

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'; then
    jq -n '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"git commit auto-approved (heredoc format)"}}'
fi
exit 0
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/git-commit-allow.sh" }]
    }]
  }
}

The hook matches on git commit as the first line, so both the heredoc format and simple -m "message" format are auto-approved. The $() substitution is irrelevant to the hook — it only looks at the command name, not the shell syntax.

ashwin-ant collaborator · 4 months ago

This was fixed in v2.1.71 — Fixed false-positive permission prompts for compound bash commands containing heredoc commit messages (e.g. git commit -m "$(cat <<'EOF'...)"). If you're still seeing this in the latest version, please comment with your version and repro and we'll reopen.

github-actions[bot] · 4 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.