[BUG] PostToolUse hooks do not fire on failed Bash commands (non-zero exit code)

Status Fixed / completed
Reported on v2.1.63
Maintainer reply ✓ Yes — dicksontsai
Activity 4 comments · opened Mar 1, 2026 · closed Mar 3, 2026
💡 Likely answer: A maintainer (dicksontsai, 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?

PostToolUse hooks do not fire when a Bash command exits with a non-zero exit code. The hook only fires on successful (exit 0) tool completions, silently skipping failures.

This was previously reported in #16282 (auto-closed as stale on 2026-03-01), #16599, and #6371. The bug is still present in v2.1.63. Filing fresh per the stale-bot's instructions.

What Should Happen?

PostToolUse should fire after all Bash tool completions, including failures. The hook payload should include success/failure context (e.g., "success": false, exit code, stderr) so hook consumers can distinguish outcomes.

A "post" hook that only fires on success is semantically a PostToolUseSuccess hook — not a general post-hook. Every standard use case for post-event hooks (logging, cleanup, analytics, error handling) requires firing on all completions.

Error Messages/Logs

# Hook log after running 3 Bash commands (success, failure, success):
12:31:17 PostToolUse fired for: Bash    ← echo PASS_1 (exit 0)
                                        ← ls ./nonexistent_xyz (exit 1) — MISSING
12:31:22 PostToolUse fired for: Bash    ← echo PASS_2 (exit 0)

# Expected: 3 entries. Actual: 2. Failed command produced no hook invocation.

Steps to Reproduce

Minimal, self-contained reproduction using only claude CLI and .claude/settings.json:

# 1. Setup
mkdir -p /tmp/hook-bug-repro/.claude

cat > /tmp/hook-bug-repro/hook.sh << 'HOOKEOF'
#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_name','?'))" 2>/dev/null)
echo "$(date '+%H:%M:%S') PostToolUse fired for: $TOOL" >> /tmp/hook-bug-repro/hook.log
HOOKEOF
chmod +x /tmp/hook-bug-repro/hook.sh

cat > /tmp/hook-bug-repro/.claude/settings.json << 'SETEOF'
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{"type": "command", "command": "bash /tmp/hook-bug-repro/hook.sh"}]
      }
    ]
  }
}
SETEOF

# 2. Run (3 Bash commands: success → failure → success)
cd /tmp/hook-bug-repro
rm -f hook.log
claude -p 'Run these 3 bash commands in separate tool calls:
1. echo PASS_1
2. ls ./nonexistent_xyz
3. echo PASS_2
Command 2 WILL fail — that is intentional.' --allowedTools 'Bash(command:*)'

# 3. Verify
cat /tmp/hook-bug-repro/hook.log
# Expected: 3 log entries (one per command)
# Actual:   2 log entries (failed command skipped)

Claude Model

Opus

Is this a regression?

I don't know

Claude Code Version

2.1.63 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

iTerm2

Additional Information

Prior issues (all still unresolved):

  • #16282 — Original report, auto-closed as stale (closing comment)
  • #16599 — Consolidated into #16282
  • #6371 — Original tracking issue

Use cases blocked by this bug:

  • Automated failure logging / learning-from-mistakes workflows
  • Hook-based error detection and notification (e.g., sound alerts on failure)
  • CI-style failure reporting
  • Analytics that need complete tool usage data
  • Automated remediation hints based on stderr parsing

Suggested fix (Option B from #16282): Extend PostToolUse to always fire, with a success boolean and failure context:

{
  "tool_name": "Bash",
  "tool_input": {"command": "ls ./nonexistent"},
  "success": false,
  "tool_response": {
    "stdout": "",
    "stderr": "ls: ./nonexistent: No such file or directory",
    "exit_code": 1
  }
}

As noted in #16282 comment, a trigger field ("success" | "failure" | "both", defaulting to "success") would preserve backward compatibility for existing hooks.

View original on GitHub ↗

4 Comments

github-actions[bot] · 7 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/6371
  2. https://github.com/anthropics/claude-code/issues/16599
  3. https://github.com/anthropics/claude-code/issues/16282

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

Elijas · 7 months ago

related issues are not duplicate
because the issue is still not solved at all

dicksontsai collaborator · 7 months ago

You'll need to use the PostToolUseFailed hook to handle failed Bash commands. PostToolUse hooks only trigger on successful tool calls.

Docs: https://code.claude.com/docs/en/hooks#posttooluse

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