HookStack
Back to catalogue
ContextPreToolUse· Read

Binary file read blocker

Stop Claude from wasting tokens trying to read .exe, .pyc, .pkl or .zip files

Intercepts Read tool calls on binary files (executables, compiled bytecode, archives, databases, ML model files, WebAssembly) and immediately blocks them with a helpful message. Claude cannot process binary content meaningfully — this hook prevents it from consuming tokens on garbage data and suggests Bash alternatives like `file` or `ls -lh`.

What does the Binary file read blocker hook do?

Binary file read blocker is a Claude Code PreToolUse hook matching Read. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. Stop Claude from wasting tokens trying to read .exe, .pyc, .pkl or .zip files.

As a PreToolUse hook it runs before the action completes, so it can block or adjust what Claude is about to do. Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.

Use cases

  • Preventing token waste on .pyc/.class bytecode files
  • Blocking reads of ML model weights (.pkl, .pt, .onnx)
  • Stopping accidental reads of archives or compiled binaries

Tags

#context-optimization#security#binary#tokens#productivity

settings.json fragment

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/pre-read-block-binary.mjs",
            "type": "command"
          }
        ],
        "matcher": "Read"
      }
    ]
  }
}

Script · .claude/hooks/pre-read-block-binary.mjs

#!/usr/bin/env node
// @hookstack pre-read-block-binary
// Bloque les lectures de fichiers binaires inutilisables par Claude (PreToolUse Read)
import { readFileSync } from "node:fs";
import { basename, extname } from "node:path";
import { fileURLToPath } from "node:url";

const BINARY_EXTENSIONS = new Set([
	// Exécutables et librairies compilées
	"exe",
	"dll",
	"so",
	"dylib",
	"bin",
	// Bytecode compilé
	"pyc",
	"pyo",
	"pyd",
	"class",
	"o",
	"a",
	"lib",
	"obj",
	// Archives
	"zip",
	"tar",
	"gz",
	"bz2",
	"xz",
	"7z",
	"rar",
	"jar",
	"war",
	"ear",
	// Bases de données
	"db",
	"sqlite",
	"sqlite3",
	// Modèles ML / artefacts
	"pkl",
	"pickle",
	"pt",
	"pth",
	"h5",
	"pb",
	"onnx",
	"npy",
	"npz",
	// WebAssembly et autres
	"wasm",
	"node",
]);

export function run(input) {
	if (input.tool_name !== "Read") return null;

	const filePath = input.tool_input?.file_path ?? "";
	if (!filePath) return null;

	const ext = extname(filePath).toLowerCase().replace(".", "");
	if (!BINARY_EXTENSIONS.has(ext)) return null;

	const name = basename(filePath);
	return {
		decision: "block",
		reason: `[block-binary] \`${name}\` is a binary file (.${ext}) — Claude cannot process it meaningfully. Inspect metadata with Bash instead (e.g. \`file\`, \`ls -lh\`).`,
	};
}

/* v8 ignore next 5 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
	const input = JSON.parse(readFileSync(0, "utf8"));
	const result = run(input);
	if (result) process.stdout.write(JSON.stringify(result));
}

Learn more

Related hooks