PreToolUse hooks: two Windows bugs — echo corrupts JSON, pre-approved permissions bypass hooks

Resolved 💬 4 comments Opened Apr 7, 2026 by PhanAnh-V Closed May 19, 2026

Environment

  • Claude Code (VS Code extension + CLI)
  • Windows 11 Home, Git Bash
  • Python 3.13

Bug 1: echo "$VAR" in bash hooks corrupts JSON backslashes on Windows

Description

PreToolUse hooks receive tool input as JSON via stdin. On Windows, file paths contain backslashes that are properly escaped in the JSON (\). However, when the hook script does:

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | python -c "import sys,json; ...")

echo interprets \ as \ before Python sees it, producing invalid JSON escapes like \U, \h, \p. Python's json.load() fails silently (stderr redirected), the variable becomes empty, and the hook falls through to exit 0 — silently allowing the tool call.

Expected behavior

Hook scripts should be able to reliably parse the JSON input on Windows.

Workaround

Skip JSON parsing entirely and grep the raw input:

INPUT=$(cat)
if echo "$INPUT" | grep -q '"file_path".*skills'; then
  echo "blocked" >&2
  exit 2
fi

Suggestion

Consider passing tool input via a temp file or environment variable instead of stdin, or document that echo is unsafe for re-piping JSON on Windows and recommend printf '%s' or heredoc.

---

Bug 2: Pre-approved Edit permissions bypass PreToolUse hooks entirely

Description

When ~/.claude/settings.json contains both:

  1. A PreToolUse hook on Edit|Write:
"hooks": {
  "PreToolUse": [{
    "matcher": "Edit|Write",
    "hooks": [{ "type": "command", "command": "bash /path/to/hook.sh" }]
  }]
}
  1. Pre-approved Edit permissions:
"permissions": {
  "allow": ["Edit(~/.claude/skills/**)"]
}

The hook never fires for files matching the pre-approved pattern. The permission check short-circuits the hook execution.

Expected behavior

Hooks should fire regardless of permission settings. A user may want to allow edits without interactive prompting AND run a hook that adds context or warnings. These are orthogonal concerns — permissions answer "is this allowed?" while hooks answer "what should happen when this runs?"

Impact

This makes it impossible to build a "read skill-map before editing any skill" guardrail if skills are pre-approved for editing. The user must choose between:

  • Pre-approve edits (convenient) but lose hook enforcement
  • Remove pre-approval (annoying permission prompts every edit) to get hooks

Reproduction

  1. Add a PreToolUse hook on Edit|Write that writes to a log file
  2. Add Edit(~/.claude/skills/**) to permissions.allow
  3. Edit a file in ~/.claude/skills/
  4. Observe: no log file created, hook never ran

---

🤖 Generated with Claude Code

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗