debug-hooks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Debug Hooks

调试Hook

Systematic workflow for debugging Claude Code hooks.
用于调试Claude Code Hook的系统化工作流。

When to Use

适用场景

  • "Hook isn't firing"
  • "Hook produces wrong output"
  • "SessionEnd not working"
  • "PostToolUse hook not triggering"
  • "Why didn't my hook run?"
  • Hook未触发
  • Hook输出错误结果
  • SessionEnd无法正常工作
  • PostToolUse Hook未触发
  • 我的Hook为什么没有运行?

Workflow

工作流

1. Check Outputs First (Observe Before Editing)

1. 先检查输出(编辑前先观察)

bash
undefined
bash
undefined

Check project cache

检查项目缓存

ls -la $CLAUDE_PROJECT_DIR/.claude/cache/
ls -la $CLAUDE_PROJECT_DIR/.claude/cache/

Check specific outputs

检查特定输出

ls -la $CLAUDE_PROJECT_DIR/.claude/cache/learnings/
ls -la $CLAUDE_PROJECT_DIR/.claude/cache/learnings/

Check for debug logs

检查调试日志

tail $CLAUDE_PROJECT_DIR/.claude/cache/*.log 2>/dev/null
tail $CLAUDE_PROJECT_DIR/.claude/cache/*.log 2>/dev/null

Also check global (common mistake: wrong path)

同时检查全局配置(常见错误:路径错误)

ls -la ~/.claude/cache/ 2>/dev/null
undefined
ls -la ~/.claude/cache/ 2>/dev/null
undefined

2. Verify Hook Registration

2. 验证Hook注册情况

bash
undefined
bash
undefined

Project settings

项目设置

cat $CLAUDE_PROJECT_DIR/.claude/settings.json | grep -A 20 '"SessionEnd"|"PostToolUse"|"UserPromptSubmit"'
cat $CLAUDE_PROJECT_DIR/.claude/settings.json | grep -A 20 '"SessionEnd"|"PostToolUse"|"UserPromptSubmit"'

Global settings (hooks merge from both)

全局设置(Hook会从两处合并)

cat ~/.claude/settings.json | grep -A 20 '"SessionEnd"|"PostToolUse"|"UserPromptSubmit"'
undefined
cat ~/.claude/settings.json | grep -A 20 '"SessionEnd"|"PostToolUse"|"UserPromptSubmit"'
undefined

3. Check Hook Files Exist

3. 检查Hook文件是否存在

bash
undefined
bash
undefined

Shell wrappers

Shell包装器

ls -la $CLAUDE_PROJECT_DIR/.claude/hooks/*.sh
ls -la $CLAUDE_PROJECT_DIR/.claude/hooks/*.sh

Compiled bundles (if using TypeScript)

编译后的打包文件(如果使用TypeScript)

ls -la $CLAUDE_PROJECT_DIR/.claude/hooks/dist/*.mjs
undefined
ls -la $CLAUDE_PROJECT_DIR/.claude/hooks/dist/*.mjs
undefined

4. Test Hook Manually

4. 手动测试Hook

bash
undefined
bash
undefined

SessionEnd hook

SessionEnd Hook

echo '{"session_id": "test-123", "reason": "clear", "transcript_path": "/tmp/test"}' |
$CLAUDE_PROJECT_DIR/.claude/hooks/session-end-cleanup.sh
echo '{"session_id": "test-123", "reason": "clear", "transcript_path": "/tmp/test"}' |
$CLAUDE_PROJECT_DIR/.claude/hooks/session-end-cleanup.sh

PostToolUse hook (Write tool example)

PostToolUse Hook(Write工具示例)

echo '{"tool_name": "Write", "tool_input": {"file_path": "test.md"}, "session_id": "test-123"}' |
$CLAUDE_PROJECT_DIR/.claude/hooks/handoff-index.sh
undefined
echo '{"tool_name": "Write", "tool_input": {"file_path": "test.md"}, "session_id": "test-123"}' |
$CLAUDE_PROJECT_DIR/.claude/hooks/handoff-index.sh
undefined

5. Check for Silent Failures

5. 检查静默失败情况

If using detached spawn with
stdio: 'ignore'
:
typescript
// This pattern hides errors!
spawn(cmd, args, { detached: true, stdio: 'ignore' })
Fix: Add temporary logging:
typescript
const logFile = fs.openSync('.claude/cache/debug.log', 'a');
spawn(cmd, args, {
  detached: true,
  stdio: ['ignore', logFile, logFile]  // capture stdout/stderr
});
如果使用了带有
stdio: 'ignore'
的分离式spawn:
typescript
// 这种模式会隐藏错误!
spawn(cmd, args, { detached: true, stdio: 'ignore' })
修复方案: 添加临时日志记录:
typescript
const logFile = fs.openSync('.claude/cache/debug.log', 'a');
spawn(cmd, args, {
  detached: true,
  stdio: ['ignore', logFile, logFile]  // 捕获stdout/stderr
});

6. Rebuild After Edits

6. 编辑后重新构建

If you edited TypeScript source, you MUST rebuild:
bash
cd $CLAUDE_PROJECT_DIR/.claude/hooks
npx esbuild src/session-end-cleanup.ts \
  --bundle --platform=node --format=esm \
  --outfile=dist/session-end-cleanup.mjs
Source edits alone don't take effect - the shell wrapper runs the bundled
.mjs
.
如果你编辑了TypeScript源码,必须重新构建:
bash
cd $CLAUDE_PROJECT_DIR/.claude/hooks
npx esbuild src/session-end-cleanup.ts \
  --bundle --platform=node --format=esm \
  --outfile=dist/session-end-cleanup.mjs
仅编辑源码不会生效——Shell包装器运行的是打包后的
.mjs
文件。

Common Issues

常见问题

SymptomLikely CauseFix
Hook never runsNot registered in settings.jsonAdd to correct event in settings
Hook runs but no outputDetached spawn hiding errorsAdd logging, check manually
Wrong session IDUsing "most recent" queryPass ID explicitly
Works locally, not in CIMissing dependenciesCheck npx/node availability
Runs twiceRegistered in both global + projectRemove duplicate
症状可能原因修复方案
Hook从未运行未在settings.json中注册在设置中添加到对应事件
Hook运行但无输出分离式spawn隐藏了错误添加日志记录,手动检查
会话ID错误使用了“最近的”查询显式传递ID
本地可用,CI环境不可用缺少依赖项检查npx/node是否可用
运行两次同时在全局和项目中注册移除重复项

Debug Checklist

调试检查清单

  • Outputs exist? (
    ls -la .claude/cache/
    )
  • Registered? (
    grep -A10 '"hooks"' .claude/settings.json
    )
  • Files exist? (
    ls .claude/hooks/*.sh
    )
  • Bundle current? (
    ls -la .claude/hooks/dist/
    )
  • Manual test works? (
    echo '{}' | ./hook.sh
    )
  • No silent failures? (check for
    stdio: 'ignore'
    )
  • 输出文件是否存在?(
    ls -la .claude/cache/
    )
  • 是否已注册?(
    grep -A10 '"hooks"' .claude/settings.json
    )
  • 文件是否存在?(
    ls .claude/hooks/*.sh
    )
  • 打包文件是否为最新版本?(
    ls -la .claude/hooks/dist/
    )
  • 手动测试是否可行?(
    echo '{}' | ./hook.sh
    )
  • 无静默失败?(检查是否使用了
    stdio: 'ignore'

Source Sessions

参考会话

Derived from 10 sessions (83% of all learnings):
  • a541f08a, 1c21e6c8, 6a9f2d7a, a8bd5cea, 2ca1a178, 657ce0b2, 3998f3a2, 2a829f12, 0b46cfd7, 862f6e2c
源自10个会话(占所有经验的83%):
  • a541f08a, 1c21e6c8, 6a9f2d7a, a8bd5cea, 2ca1a178, 657ce0b2, 3998f3a2, 2a829f12, 0b46cfd7, 862f6e2c