docs: hooks-guide auto-format example breaks on paths with spaces and exits 2 on unsupported files — contradicts the bundled update-config skill

Status Open
Reported on v2.1.237
Maintainer reply None cached
Activity 0 comments · opened Aug 20, 2026

Summary

The "Auto-format code after edits" example in hooks-guide has two defects. It appears twice on the page and is the first hook pattern most people copy.

"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"

Both failures are silent or noisy in ways that surface long after the user has stopped watching the hook.

Defect 1 — xargs word-splits paths containing spaces

xargs splits on whitespace, so one file becomes several nonexistent paths.

$ echo '{"tool_input":{"file_path":"/tmp/carpeta con espacios/a.ts"}}' \
    | jq -r '.tool_input.file_path' | xargs printf '[%s]\n'
[/tmp/carpeta]
[con]
[espacios/a.ts]

$ echo '{"tool_input":{"file_path":"/tmp/carpeta con espacios/a.ts"}}' \
    | jq -r '.tool_input.file_path' | xargs ls
ls: /tmp/carpeta: No such file or directory
ls: con: No such file or directory
ls: espacios/a.ts: No such file or directory

Control: with a path that has no spaces, xargs works correctly. The bug is therefore intermittent by nature — the hook appears to work for months until someone edits a file under My Project/.

The page contains no warning about spaces or word-splitting. Searched the full 71 KB of the rendered page: zero occurrences of read -r, and nothing about quoting near the example. The only related note is 900 lines away under troubleshooting ("command not found"), suggesting "args": [] for exec form.

Defect 2 — no --ignore-unknown, so unsupported files exit 2

exit 2 is the _blocking error_ code for hooks: stderr is fed back to Claude.

$ prettier --write s.py                    ; echo "exit=$?"
[error] No parser could be inferred for file ".../s.py".
exit=2

$ prettier --write --ignore-unknown s.py   ; echo "exit=$?"
exit=0

The documented command has neither --ignore-unknown nor || true. In any polyglot repository, every edit to a .py, .sh, .toml, or any file Prettier has no parser for makes the hook return a blocking error on an operation that succeeded.

Defect 3 (minor) — npx can hit the network

npx prettier with no local copy resolves by downloading from the registry. On a machine whose global Prettier lives inside an nvm release, a Node version bump removes it from PATH and this hook silently starts fetching Prettier on every edit.

This contradicts a bundled first-party source

The update-config skill that ships with Claude Code prescribes the opposite, explicitly:

use jq -r into a quoted variable or { read -r f; ... "$f"; }, NOT unquoted | xargs (splits on spaces)

Two official sources of the same product disagree, and the more visible one is the incorrect one.

Suggested fix

"command": "jq -r '.tool_response.filePath // .tool_input.file_path' | { read -r f; npx prettier --write --ignore-unknown \"$f\"; } || true"
  • read -r into a quoted variable — no word-splitting
  • --ignore-unknown — unsupported files are skipped silently instead of exiting 2 (also honours .prettierignore)
  • || true — a formatter should not turn a successful edit into a hook error
  • .tool_response.filePath first, matching the fallback the update-config skill uses

A one-line note that paths may contain spaces would also help readers who adapt the pattern to other formatters.

Environment

  • Claude Code 2.1.237
  • macOS (arm64), APFS
  • Prettier 3.9.6
  • jq via Homebrew

Also worth checking

The page states "When the hook succeeds, Claude Code shows nothing in the conversation." In practice a PostToolUse hook that rewrites the edited file surfaces a PostToolUse hook modified <file> after your edit (likely a formatter) notice. I have not isolated the exact conditions, so this is an observation rather than a confirmed claim — but the sentence may be worth revisiting, since that notice is genuinely useful and users are being told it does not exist.

View original on GitHub ↗