PreToolUse hooks never fire for Read on binary files (binary rejection precedes hook execution)

Status Open
Maintainer reply None cached
Activity 1 comment · opened Jul 30, 2026

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

  1. Register a PreToolUse hook in ~/.claude/settings.json:

``json
{ "hooks": { "PreToolUse": [ { "matcher": "Read",
"hooks": [ { "type": "command", "command": "/abs/path/hook.sh", "timeout": 30 } ] } ] } }
``

  1. 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"}}
``

  1. Restart Claude Code (hooks load at session start).
  2. Read a .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.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗