Claude Code hook 在 Windows 上不起作用:原因与解决方法

同一个 hook 在 Mac 上运行正常,到了 Windows 上却毫无反应,甚至更糟,拦下所有命令。罪魁祸首几乎总是 shell。下面介绍 Claude Code 在 Windows 上如何运行 hook,以及如何让你的 hook 稳定可靠。

作者:Aslan,Pet Island 团队 · 更新于 2026年10月26日 · 阅读时长 5 分钟

简短回答

在 Windows 上,Claude Code 使用 Git Bash 运行 hook 的 command;如果没有安装 Git Bash,则使用 PowerShell。用 "shell": "powershell" 或 "shell": "bash" 明确指定要用哪一个,让 stdout 只输出一个 JSON 对象(不要有配置文件的 echo 输出),用退出码 2 来拦截,并更新 Claude Code(截至 2.1.310 的多个版本修复了若干 Windows hook 缺陷)。使用 /hooks 和 claude --debug 进行调试。

由哪个 shell 运行你的 hook

Claude Code 会把 command 字符串交给一个 shell:macOS 和 Linux 上是 sh -c,Windows 上是 Git Bash,没有安装 Git Bash 时则是 PowerShell。hook 参考文档中的这一句话,就解释了大多数“在我的 Mac 上没问题”的反馈。为 PowerShell 编写的命令在 Git Bash 中会失败,bash 脚本在 PowerShell 中会失败,而你实际用的是哪一个,取决于电脑上恰好有没有安装 Git for Windows。

用 shell 字段消除不确定性。它接受 "bash" 或 "powershell":

%USERPROFILE%\.claude\settings.json复制
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"shell": "powershell",
"command": "[Console]::Beep(880, 200)"
}
]
}
]
}
}

如果你想用 Git Bash 而 Claude Code 找不到它,请安装 Git for Windows,或将 CLAUDE_CODE_GIT_BASH_PATH 设为其所在位置,然后开启新会话。

症状及其原因

你看到的现象可能的原因解决方法
hook 似乎从未运行Claude Code 版本过旧、事件或 matcher 写错、设置中的 JSON 无效更新;检查 /hooks;删除注释和末尾逗号
ParserError 或“unexpected token”PowerShell 语法被交给了 Git Bash,或者反过来显式设置 shell
hook 输出“does not start with {”(不是以 { 开头)JSON 之前打印了其他内容,常见来源是 shell 配置文件屏蔽配置文件的输出;把日志发送到 stderr
混用 \ 和 / 的路径找不到Windows 路径变量与 Unix 风格的后缀拼接在一起使用 exec 形式:程序写在 command 中,每个参数写在 args 中
Git Bash 报出 powershell: Permission denied在 2.1.283 和 2.1.304 至 2.1.305 中有间歇性报告(#103910,尚未关闭)使用 "shell": "powershell",让 Git Bash 不再夹在中间
防护 hook 放行了所有操作退出码用了 1 而不是 2,或旧版本中存在失败即放行(fail-open)的缺陷用退出码 2 拦截;更新到 2.1.310 或更高版本
hook 立即返回且没有输出GUI 子系统程序,shell 不会等待它结束改用控制台程序,或等待该进程结束

stdout 中只输出 JSON

Claude Code 会把 hook 的标准输出当作它的答复。文档中的规则很简单:除了 JSON 对象,不要向 stdout 打印任何内容。在 Windows 上,这一点会以一种隐蔽的方式出问题:Git Bash 和某些环境仍会加载你的 shell 配置文件,而配置文件中无条件执行的 echo 会被加在你的 JSON 前面。于是 hook 在终端里“能用”,在 Claude Code 中却失败。

请改为把日志写到文件或 stderr,并让配置文件只在交互式 shell 中才产生输出。

退出码:2 会拦截,1 不会

退出码 0 表示成功。退出码 2 表示阻塞性错误:对于可以拦截的事件,Claude Code 会中止该操作并显示你的 stderr。退出码 1 且没有有效 JSON 时属于非阻塞性错误,操作会继续执行。 如果你的 hook 用于执行策略,请使用退出码 2。

并非所有事件都能用这种方式拦截。Notification hook 的退出码会被忽略。Stop hook 以 2 退出会让 Claude 继续工作。PermissionRequest hook 完全忽略退出码 2,它通过 JSON 中的 decision 对象来做决定。

Claude Code 2.1.309 修复了 hook 程序以退出码 2 退出时 PowerShell hook 无法拦截的问题。如果你的 PowerShell 防护似乎被忽略了,请先检查版本。

值得为之更新的 Windows 修复

Anthropic 的更新日志中有一长串 Windows hook 修复。以下几项可以解释论坛上的旧帖:

版本修复内容
2.1.47PreToolUse 和 PostToolUse hook 在 Windows 上静默失败;现在改用 Git Bash 而不是 cmd.exe 运行
2.1.161显式调用 bash 的 hook(/usr/bin/bash script.sh)报错“command not found”
2.1.299未设置 shell 的 PreToolUse 和 PermissionRequest hook 在没有 Git for Windows 时会失败放行;现在失败的防护会执行拦截
2.1.309PowerShell hook 遇到退出码 2 时不拦截;在没有 Git for Windows 时,"shell": "powershell" 防护会失败放行
2.1.310命令仅由带引号的字符串(例如带引号的路径)构成的防护,在没有 Git Bash 时会失败放行

较早的那个标题为“No hook is working on Windows”的 issue(#10450)是在 2.0.27 上报告的;一位评论者查明原因是 stdin 没有传到 hook 命令。如果你的版本这么旧,请先更新再调试其他问题。

直接调用程序

如果你的 hook 是一个编译好的程序,可以用 exec 形式跳过 shell(即 args 字段;设置了 args 时 shell 会被忽略)。在 Windows 上,exec 形式要求 command 解析为真正的可执行文件,例如 .exe;.cmd 和 .bat 包装脚本(包括 npm 安装的大多数东西)不能在没有 shell 的情况下启动。请指向 .exe,或者使用 shell 形式。

我们在开发自己的 hook 辅助程序时得到一条教训:在 Windows 上,shell 启动为 GUI 子系统编译的程序时不会等待它结束。shell 会立即返回,智能体读到的是空输出,hook 看上去毫无反应。hook 程序必须是控制台程序。

插件 hook 与路径

在 v2.1.2 到 v2.1.12 之间,拼接 ${CLAUDE_PLUGIN_ROOT}/hooks/run.sh 这类路径的插件 hook 在 Windows 上无法运行(#18610):该变量展开后是反斜杠路径,再加上正斜杠后缀,就成了 bash 无法解析的混合路径。当前文档给出了干净的解决办法:只要 hook 引用路径占位符,就使用 exec 形式。把程序放在 command 中,每个参数放在 args 中,例如 "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js"]。每个元素都作为一个独立参数传递,不会被 shell 篡改。

调试清单

  1. /hooks 会打开一个只读列表,列出所有已配置的 hook 及其来源。
  2. claude --debug(或在会话中使用 /debug)会显示每次 hook 运行、输出和退出码。claude --debug-file <path> 会把这些内容写入文件。
  3. 验证 JSON。 设置文件是严格的 JSON;一个末尾逗号就会让整个文件失效。
  4. 检查 matcher。 matcher 区分大小写;"*"、"" 或不写 matcher 都会匹配所有内容。
  5. 自己运行一遍命令,使用 Claude Code 将使用的同一个 shell,并通过管道传入示例 JSON。
  6. 检查超时。 命令 hook 的默认超时为 600 秒;需要等人响应的 hook 需要这么长的余量。

排查期间如需关闭所有 hook,请设置 "disableAllHooks": true。

如果你只是想要通知和审批

很多 Windows hook 配置只为一个目的:知道 Claude Code 什么时候需要你,并做出回应。Pet Island 无需任何脚本就能做到这一点。它一键连接 Claude Code,替你处理好 Windows 上的细节,并把每个权限请求、提问和计划连同命令或 diff 一起放到屏幕顶部的小岛上,你在那里允许或拒绝即可。Claude Code 在 WSL 中运行时它也能工作。

常见问题

Windows 上由哪个 shell 运行 Claude Code hook?

Git Bash;如果没有安装 Git Bash,则是 PowerShell。在 hook 中设置 "shell": "powershell" 或 "shell": "bash" 可以明确指定。

为什么我的 Claude Code hook 在终端中可以运行,在 Claude Code 中却不行?

通常是多余的输出:会 echo 文本的 shell 配置文件把内容加在了 hook 的 JSON 前面。stdout 中只打印 JSON 对象。

如何让 hook 拦截工具调用?

以退出码 2 退出(或在 JSON 中返回 deny 决定)。退出码 1 是非阻塞性错误,操作会继续执行。PermissionRequest hook 会忽略退出码 2,改用 decision 对象。

hook 命令可以是 .cmd 或 .bat 文件吗?

exec 形式下不行。在 Windows 上,它必须解析为真正的可执行文件,例如 .exe。要运行 .cmd 或 .bat 文件,请使用 shell 形式。

如何查看 hook 失败的原因?

运行 claude --debug 或输入 /debug,并用 /hooks 查看加载了哪些 hook。

参考来源

本页的每项设置和行为都已于 2026年10月26日 对照以下页面核实。智能体更新很快;如有不符,以官方页面为准。

继续阅读

在屏幕顶部回应每一个智能体。

Windows 和 Mac · 免费试用 7 天,之后一次性支付 8.99 美元 · 每笔销售拿出 1 美元为流浪动物提供食物

免费试用 Pet Island