Which failures count
A tool can fail in several ways, and this event does not cover all of them. PostToolUseFailure fires for a tool that started and then failed, an MCP tool returning an error result included. It does not fire for an unknown tool name, an invalid input, a permission denial, or a running tool you cancelled. The matcher filters tool names and the if filter applies, the same as on PostToolUse.
The error is display text
The payload carries permission_mode, tool_name, tool_input, tool_use_id, error, an optional is_interrupt, and an optional duration_ms. The hooks reference calls the error string display text rather than a stable schema. A hook that parses it for structure is depending on wording the reference does not promise to keep.
Context after the failure
The failure is already in the transcript. Your hook decides what Claude reads next to it. Exit 0 leaves the failed result in place and applies hookSpecificOutput.additionalContext on top of it. Exit 1 leaves the error in place, and the transcript gains a second one, the hook’s. Exit 2 cannot block a thing that has already failed, but its stderr is shown to Claude as corrective feedback. In every case the tool failed and stays failed.
The reference disagrees with itself
Its decision-control summary table lists this event with PostToolUse as honouring a top-level decision: "block" and reason. The event’s own section documents only additionalContext and gives no meaning for the pair. The two parts of the hooks reference disagree here. Pick one reading and verify it against a real run before you depend on it.
A test run fails, and your hook exits 2 with “re-run with —verbose” on stderr, expecting a retry.
No retry happens. Claude reads the sentence as feedback beside the error and decides what to do with it, which may be to run the command again and may be to explain the failure and stop. Exit 2 on this event delivers a message. It does not re-run anything, and the transcript shows the original failure either way.
If the retry has to happen, the hook runs the command itself and reports the outcome in additionalContext.
Module 09 lists this event among the five where the if filter applies.