PRODUCTION SCENARIO
Your PreToolUse hook script matches on Bash, prints JSON on stdout with permissionDecision set to allow for approved database commands, and returns in well under its timeout. A stray error path makes the script exit with code 2 after printing. Engineers see those approved commands blocked, and the reason shown in the transcript is the script's own stderr text.
What explains the blocked commands?
Answering here is anonymous. Nothing is saved unless you sign in.
Show answer and explanation
Answer: Exit code 2 is a blocking error whatever the JSON says, so the script must exit 0
On events that can block, exit 2 blocks whether or not the hook printed JSON, and an allow decision cannot override it; the blocking message is the reason from a blocking decision in the JSON when there is one, and the stderr text otherwise, which is exactly what the engineers are seeing. Exit 0 is the intended code for a hook that prints JSON for structured control. A discarded output is what a stalled hook produces, and a timed-out PreToolUse hook lets the call continue rather than blocking it. JSON on the wrong stream and a matcher that did not cover the tool each explain a decision that goes missing, and neither would put this script's stderr in the transcript.
Official Source: https://code.claude.com/docs/en/hooks ↗