Claude Code Hooks 配置了但不触发:六步排查方法
Claude Code hooks 不触发通常有六个原因:配置文件位置错、matcher 写法错、脚本没有可执行权限、退出码不对、macOS 通知权限未授权,或 hook 触发了但命令本身失败。本文按优先级排查。

Claude Code hooks 是在特定事件时自动运行 shell 命令的机制——比如每次文件编辑后自动格式化、在 Claude 等待输入时发桌面通知、在危险命令执行前拦截。配置写了但 hook 不触发,原因通常很具体,不是 Claude Code 本身的 bug,而是配置细节出了问题。
第一步:用 /hooks 确认 hook 是否已被加载
在 Claude Code CLI 里输入 /hooks,会打开一个 hook 浏览器,列出所有已加载的 hook 事件(每个有配置的事件旁边显示数量)。
如果你配置的 hook 没有出现在这里,说明配置根本没有被读取。常见原因:
- 配置文件位置错了
- JSON 格式有语法错误
- 使用了错误的 hook 事件名
如果 hook 出现在列表里了,但还是没有触发,问题在 matcher 或脚本本身,继续下一步。
注意:/hooks 菜单是只读的,无法在菜单里修改配置,需要直接编辑 settings JSON 文件。
第二步:检查配置文件的位置
Claude Code 的 hooks 可以配置在两个位置,优先级不同:
| 文件位置 | 作用范围 |
|---|---|
~/.claude/settings.json |
全局,适用于所有项目 |
[项目目录]/.claude/settings.json |
仅当前项目 |
最常见的错误:把项目级的 settings.json 放在了错误的目录(比如放在项目根目录而不是 .claude/ 子目录里)。确认路径是 .claude/settings.json,不是直接在项目根目录的 settings.json。
正确的 JSON 结构:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "your-command-here"
}
]
}
]
}
}
注意:hooks 是顶层键,事件名(PostToolUse、Notification 等)是 hooks 对象的子键。结构嵌套方式容易出错。
第三步:验证 matcher 写法
Matcher 决定 hook 在哪个工具调用时触发。写法错误会导致 hook 注册成功但永远不匹配:
PreToolUse / PostToolUse 的 matcher 是工具名,支持 | 分隔多个(Claude Code v2.1.191+ 也支持 ,):
- 匹配所有文件编辑:
"Edit|Write" - 只匹配 Bash 执行:
"Bash" - 匹配所有工具(空字符串):
""
Notification 的 matcher 是通知类型,不是工具名:
- 所有通知:
""或空 - 只在等待输入时:
"idle_prompt" - 只在需要授权时:
"permission_prompt"
常见错误:在 Notification hook 里写了 "Edit|Write" 这样的工具名 matcher,导致永远不触发。
第四步:检查脚本的可执行权限
如果 hook command 调用的是一个脚本文件(而不是内联命令),脚本必须有可执行权限:
chmod +x .claude/hooks/your-script.sh
忘记 chmod +x 是最容易犯也最难察觉的错误之一,因为 Claude Code 不会报"权限不足"的明显错误,hook 只是默默不运行。
确认脚本在终端里能单独执行:
echo '{"tool_input":{"file_path":"test.js"}}' | .claude/hooks/your-script.sh
能运行、退出码符合预期才算配置正确。
第五步:确认退出码语义
Claude Code hooks 的退出码有明确的语义,写错会导致预期之外的行为:
| 退出码 | 含义 | 适用场景 |
|---|---|---|
0 |
成功,继续执行 | 格式化、日志、通知 |
1 |
失败,向 Claude 报告错误消息(stderr) | 警告但不阻断 |
2 |
阻断——不执行这次工具调用 | PreToolUse 里阻止危险命令 |
典型错误场景:
- 写了一个
PreToolUse文件保护 hook,想拦截对.env的修改,但脚本在匹配时返回了exit 1而不是exit 2——结果是 Claude 收到了警告但仍然继续修改文件。 - 在
PostToolUse里写了一个格式化 hook,格式化命令失败时返回非零退出码,Claude 把这解读为格式化出了问题,在 session 里产生了不必要的错误信息。
第六步:macOS Notification Hook 的特殊权限问题
如果你配置了 macOS 桌面通知 hook,用的是 osascript,通知不出现通常是系统权限问题,不是 Claude Code 问题:
osascript 通过 Script Editor 发通知,Script Editor 需要在系统设置里单独授权通知权限。这个权限不会自动提示。
修复步骤:
# 在 terminal 里运行一次,触发权限检查
osascript -e 'display notification "test"'
运行后什么都不会出现。打开 系统设置 → 通知,找到 Script Editor,开启"允许通知"。再运行一次上面的命令,确认通知出现。
配置好权限后,Claude Code 的 Notification hook 才能正常工作。
如何验证 hook 实际运行了什么
在 hook command 里加日志是最直接的排查方法:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "echo \"$(date): hook triggered for $(jq -r '.tool_input.file_path')\" >> /tmp/claude-hooks.log && npx prettier --write \"$(jq -r '.tool_input.file_path')\""
}
]
}
]
}
}
这样每次 hook 触发都会在 /tmp/claude-hooks.log 里留一条记录,可以看到触发时间和文件名,判断 hook 是触发了但命令失败,还是根本没触发。
常见问题
问:在 .claude/settings.json 里配置了 hooks,但项目里没有 .claude 目录,怎么办?
答:手动创建 .claude 目录和 settings.json 文件:mkdir -p .claude && echo '{"hooks":{}}' > .claude/settings.json,然后编辑这个文件加入你的 hooks 配置。
问:hook 的命令可以用相对路径吗?
答:可以,相对路径相对于项目根目录($CLAUDE_PROJECT_DIR)解析。官方推荐的写法是 "$CLAUDE_PROJECT_DIR"/.claude/hooks/your-script.sh,用环境变量明确路径。
问:能在一个事件上挂多个 hooks 吗?
答:可以。同一个事件的 hooks 数组可以有多个条目,按顺序执行。如果其中一个返回 exit 2,后续的 hooks 不会执行,工具调用被阻断。
问:hooks 在 subagent 里也会触发吗? 答:会。subagent 运行期间的工具调用(Edit、Write、Bash 等)同样会触发 hooks,包括项目级和全局级的配置。