Hooks and the SDKLesson 15 of 18

Gotchas Around Hooks

Common Hook Pitfalls

Hooks are powerful but have several subtle gotchas that can lead to unexpected behavior. Understanding these upfront saves significant debugging time.

GotchaWhat HappensSolution
Not restarting ClaudeHook changes are ignored silentlyAlways restart after config or script changes
Wrong tool name in matcherHook never firesAsk Claude for exact tool names, test with logging
Post-hook trying to blockExit code 2 is ignoredOnly pre-hooks can block; post-hooks just provide feedback
Missing stdin handlingScript crashes with parse errorAlways wrap JSON.parse in try/catch
Slow hook scriptClaude Code feels sluggishKeep hooks fast (<500ms); avoid network calls in pre-hooks
Wrong path fieldFile check misses some toolsCheck file_path, path, and pattern fields

Hook gotchas and their solutions

Fail-Open Design

If your hook script crashes or returns an unexpected exit code, Claude Code treats it as 'allow' (fail-open). This is a safety feature -- a buggy hook won't permanently lock Claude out of tools. But it means you should test hooks thoroughly to ensure they actually block what they're supposed to.

Debugging Hooks

When a hook isn't working as expected, add temporary logging to understand what data it receives and what decisions it makes.

javascriptDebug hook: log tool call data to a file for inspection
// Temporary debug logging for hooks
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin', 'utf8');
const data = JSON.parse(input);

// Log to a file (not stdout/stderr, which go to Claude)
fs.appendFileSync(
  './hooks/debug.log',
  JSON.stringify(data, null, 2) + '\n---\n'
);

// Now check debug.log to see exact tool call data
process.exit(0);

Log to File, Not Console

When debugging, write logs to a file rather than console.log (stdout) or console.error (stderr). Stdout is ignored by Claude Code, and stderr is treated as feedback to Claude, which can confuse the conversation.

Key Takeaways

  • Always restart Claude Code after any hook configuration or script changes
  • Hooks are fail-open: crashes or unexpected exit codes are treated as 'allow'
  • Only pre-hooks can block tool calls (exit code 2); post-hooks provide feedback only
  • Keep hook scripts fast (<500ms) to avoid making Claude Code feel sluggish
  • Debug by logging to a file, not stdout/stderr, to avoid interfering with Claude communication

Check Your Understanding

Test what you learned in this lesson.

Q1.Your hook script crashes with an unhandled exception. What happens to Claude's tool call?

Q2.When debugging a hook, why should you log to a file instead of using console.log()?

Q3.A hook that makes an API call takes 3 seconds to complete. What impact does this have?