PreToolUse hooks never fire for Read on binary files (binary rejection precedes hook execution)
PreToolUse hooks never fire for Read on binary files (binary rejection precedes hook execution)
Summary
A PreToolUse hook registered with matcher: "Read" is never invoked when Read targets a binary file (e.g. .docx, .pdf). Claude Code's Read tool rejects binary files ("This tool cannot read binary files") in a validation step that runs before PreToolUse hooks. This makes it impossible to use a PreToolUse hook to transparently convert/redirect binary reads (a common pattern: auto-convert Office/PDF → Markdown, then deny the binary read and point the model at the .md).
Expected behavior
PreToolUse hooks should run before the tool's own input validation, so a hook can permissionDecision: "deny" a binary Read and redirect the model — consistent with the documented purpose of PreToolUse (inspect/modify/deny a tool call before it executes).
Actual behavior
The binary-file rejection fires first; the hook is never called for binary Read targets. For non-binary Read targets the same hook runs normally.
Reproduction
- Register a
PreToolUsehook in~/.claude/settings.json:
``json``
{ "hooks": { "PreToolUse": [ { "matcher": "Read",
"hooks": [ { "type": "command", "command": "/abs/path/hook.sh", "timeout": 30 } ] } ] } }
- Hook script writes a marker on entry, then emits a valid deny decision:
``bash``
INPUT=$(cat)
echo "FIRED $(date)" >> /tmp/hook-fired.log
# ... emit: {"hookSpecificOutput":{"hookEventName":"PreToolUse",
# "permissionDecision":"deny","permissionDecisionReason":"read the .md instead"}}
- Restart Claude Code (hooks load at session start).
Reada.docx.
Observed: Read returns "This tool cannot read binary files"; /tmp/hook-fired.log is empty (hook never ran). Running the same script by hand (printf '...' | bash hook.sh) writes the marker and emits the correct JSON — so the hook and its output schema are valid; Claude Code simply does not invoke it for binary Read.
Environment
- Claude Code (CLI), macOS (Darwin 22.6.0)
- Hook: user scope (
~/.claude/settings.json),matcher: "Read" - Output schema used: current
hookSpecificOutput.permissionDecision: "deny"(verified correct in isolation)
Impact / use case
Blocks the "auto-convert binary docs to Markdown on Read" workflow entirely. Also observed for subagent tool calls. Workaround in use: a markitdown skill that converts explicitly before reading — but that requires the model to remember to invoke it, which is exactly what the hook was meant to make automatic.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗