- 桌面应用
- 交互助手
【免费下载链接】clawd-on-desk
A pixel desktop pet that watches Claude Code, Codex, Cursor & other AI coding agents — so you don't have to.
本文整理自 clawd-on-desk 仓库中的调查文档 docs/investigations/permission-hook-fail-deny-investigation.md,结合仓库源码与测试加以印证。这是一篇已闭环的历史排障记录:问题由上游 Claude Code 修复(v2.1.113),本文档保留原始证据链与时间线,主要服务于仍在旧版本上的用户排障,不代表 Clawd 当前行为。
导读
本文完整复盘了 Clawd(clawd-on-desk,一款监控 Claude Code / Codex / Cursor 等 AI 编程助手的像素桌宠)在 Claude Code 2.1.100 版本上遇到的一个上游缺陷:桌宠(Electron 主进程)没在运行时,Claude Code 会把 PermissionRequest HTTP hook 的端口连接失败(ECONNREFUSED)当成"用户拒绝",导致 Edit / Write / Bash 等需要权限的工具被静默自动 deny。读完本文,你将掌握:该问题的完整现象与复现方法、基于 hook 文档与permission-debug.log的证据链、两条临时绕过方案,以及 Clawd 项目为何坚持不在自己侧做 fail-deny 兜底的设计决策。
问题状态:已由上游修复,本文档归档为历史记录
调查文档(2026-09-18 更新)确认了以下版本事实:
- 受影响版本:Claude Code2.1.100(完整受影响区间未逐版验证)。
- 上游修复版本:Claude Codev2.1.113。维护者账号在 v2.1.113 起无法复现;Claude contributor 明确回复 "fixed as of v2.1.113",上游 Issue 以Completed关闭;报告者在v2.1.274再次验证不再出现 fail-deny。
- 影响范围(仅历史版本):桌宠没在跑时,Claude Code 调用 Edit/Write/Bash 等需要权限确认的工具会被自动 deny,用户看到 "tool use was rejected"。
- Clawd 侧动作:不兜底,也不因该历史问题改权限 hook 策略。在仍能复现的旧版本上,升级 Claude Code 或保持桌宠运行即可。
文档明确声明:下面的证据链、根因与时间线保留原始版本信息,仅用于旧版用户排障,不代表当前行为。
TL;DR:一句话看懂问题
Claude Code 2.1.100 给 Edit/Write/Bash 等所有需要权限的工具都发送PermissionRequesthook(matcher 与PreToolUse共享,覆盖所有工具名)。Clawd 通过 HTTP hook(http://127.0.0.1:23333/permission)收集权限决策并弹出气泡。当桌宠没在跑、端口无人监听时,CC 收到ECONNREFUSED后静默 deny tool call,而不是按官方文档承诺的 non-blocking 行为 fall through 到内置聊天确认提示。用户并没有手动点击拒绝,Claude Code 却把"hook 失败"当成了"用户拒绝"。
实际行为违反 Claude Code 自己的 hook 文档,因此当时判定为 CC 的 bug,而不是 Clawd 应该兜底的问题。
现象:桌宠离线时 Edit / Write / Bash 被立即拒绝
桌宠 Electron 主进程没在跑时,Claude Code 在任意工作目录调用 Edit / Write / Bash 等"需要权限确认"的工具,会立刻被自动拒绝,返回消息:
The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file).
关键点在于:用户没有手动点击拒绝。Claude Code 把"hook 连接失败"错误地解释成了"用户拒绝"。对于文件编辑类操作,这意味着new_string根本没有被写入文件,Claude 会反复重试,浪费一轮又一轮的调用。
根因:ECONNREFUSED 被 CC 当作拒绝信号
CC 2.1.100 现在给 Edit/Write/Bash 等所有需要权限的工具都发PermissionRequesthook(matcher 与PreToolUse共享,覆盖所有工具名)。当 hook 配置为 HTTP 类型且端口无人 listen 时,CC 收到ECONNREFUSED后会silently deny tool call,而不是按文档承诺的 non-blocking 行为 fall through 到内置 chat prompt。
这在 Clawd 仓库源码中可以找到对应的 hook 注册实现。在 hooks/install.js 中:
// HTTP hooks: PermissionRequest uses bidirectional HTTP hook for permission decisions. // Claude Code fires PermissionRequest for tools needing approval (primarily Bash). // Edit/Write permissions are handled by Claude Code's own permission mode — not our hook. const HTTP_HOOKS = { PermissionRequest: { matcher: "", hook: { type: "http", url: "http://127.0.0.1:23333/permission", timeout: 600, }, }, };即 Clawd 把PermissionRequest注册为双向 HTTP hook,超时 600 秒,端口固定为 23333。同时在 hooks/install.js 的CORE_HOOKS注释里明确:"PermissionRequest: handled by HTTP_HOOKS (blocking), not command hook"——即权限决策走 HTTP hook(阻塞式、可返回 allow/deny),而 command hook 只负责上报状态、不做任何决策(见 hooks/clawd-hook.js 的同类注释)。这正是问题发生时,一旦 23333 端口没有进程监听,CC 就会把连接失败当作"hook 给出的决策"来处理的原因。
证据链
调查文档给出了三条环环相扣的证据,最终锁定了"CC 违反自身文档"这一结论。
证据 1:CC 确实给 Write 发 PermissionRequest(实证)
%APPDATA%/clawd-on-desk/permission-debug.log行2026-04-10T10:56:59.529Z:
[2026-04-10T10:56:59.529Z] showing bubble: tool=Write session=807e3e37-...tool=Write走了/permissionHTTP 端点,证明 CC 2.1.100 确实给 Write 发PermissionRequest(而不是只给 Bash)。这条日志的格式与 Clawd 源码中的记录路径完全吻合:在 src/server-route-permission.js 中,handlePermissionPost处理完请求后通过ctx.permLog输出${agentId} showing bubble: tool=${toolName} session=${sessionId};而permLog在 src/permission.js 中通过rotatedAppend以[ISO时间] 消息的格式写入permDebugLog(即permission-debug.log)。也就是说,文档中引用的日志行正是 Clawd 自身的/permission端点被 CC 真实触发的运行记录。
证据 2:CC 官方文档承诺 HTTP hook 失败 non-blocking
来源:Claude Code 官方 hooks 文档(调查当时 2026-04-10 已 WebFetch 核实原文):
Error handling differs from command hooks: non-2xx responses, connection failures, and timeouts all produce non-blocking errors that allow execution to continue. To block a tool call or deny a permission, return a 2xx response with a JSON body containing
decision: "block"or ahookSpecificOutputwithpermissionDecision: "deny".
并且 HTTP response handling 表格里明确:
Connection failure or timeout: non-blocking error, execution continues
PermissionRequest matcher 覆盖范围(同一份文档):
Matches on tool name, same values as PreToolUse.
而 PreToolUse matcher 接受Bash、Edit|Write、mcp__.*等所有工具名。也就是说,按官方语义,连接失败应当是非阻塞的、执行继续,只有当 hook 返回 2xx 且 JSON body 明确给出decision: "block"或permissionDecision: "deny"时才允许拒绝工具调用。
证据 3:矛盾——实际行为是 fail-closed deny
桌宠没在跑 → 端口 23333 无监听 → CC 收到ECONNREFUSED→ 拒绝 tool call。这与证据 2 中官方文档承诺的 non-blocking 行为直接矛盾。
Clawd 侧实现同样遵循"绝不越权替用户做决策"的原则:例如在 src/server-route-permission.js 的注释中写明,对于子代理(subagent)的 PermissionRequest,当子代理子门控关闭时,断开 HTTP 连接让 CC 回落到原生流程,绝不替用户回答 allow/deny。而 CC 2.1.100 却把连接失败处理成了 deny,这正是问题所在。
排除掉的方向(节省未来调查时间)
调查过程中逐一排除了 Clawd 侧的几个嫌疑,避免后人重复排查:
| 方向 | 排除依据 |
|---|---|
hooks/install.js写错了 url/timeout | 历史无回归 commit;桌宠开着时 hook 完全正常工作 |
| timeout 单位从秒变成毫秒 | 文档明确写秒;如果是单位问题桌宠开着也会失败,但日志大量正常 ack |
settings.jsonjson schema 解析错误 | json 合法且其他 hook 段都正常工作 |
80a1670(DND fail-deny 修复)的回归 | 那个修复改的是桌宠运行中且 DND的代码路径,桌宠没开时根本到不了那段代码 |
091bb59(移除 PreToolUse HTTP hook for Edit/Write)的回归 | 091bb59 在 CC 旧版本下是对的;是 CC 后来升级把 Edit/Write 也纳入 PermissionRequest |
其中"移除 PreToolUse HTTP hook for Edit/Write"这一历史演进,与 hooks/install.js 中那行过时注释("Edit/Write permissions are handled by Claude Code's own permission mode — not our hook")相互印证:在旧版本 Claude Code 中 Edit/Write 权限由 CC 自身的权限模式处理,Clawd 只通过 hook 处理 Bash;是 CC 2.1.100 的升级把 Edit/Write 也纳入了 PermissionRequest,才让连接失败的问题暴露出来。
复现步骤
在受影响版本(如 2.1.100)上,按以下步骤即可稳定复现:
- 关闭桌宠(菜单退出 / kill electron 进程)
- 确认
127.0.0.1:23333无监听:netstat -ano | grep 23333 - 在任意工作目录开 Claude Code session
- 让 Claude 调用 Edit / Write / Bash 工具
- 观察:立刻被 deny
恢复方法:开桌宠 → 23333 恢复监听 → hook 正常工作 → 权限气泡弹出 → 由用户决定。
临时绕过(按推荐度排序)
针对仍停留在旧版本、暂时无法升级的用户:
- 开桌宠(已验证)—— 桌宠日常本来就开着,最简单,让 23333 端口保持监听即可。
- 临时屏蔽 hook:把
~/.claude/settings.json里的PermissionRequest段重命名为_PermissionRequest(key 改个名让 CC 找不到),走默认 Y/N 询问。之后必须改回来,否则桌宠权限气泡整体失效。
需要特别提醒:第二条只是临时逃生通道,改完忘记还原会导致桌宠的权限气泡功能彻底停摆,务必在升级或恢复桌宠后改回。
结案上下文:从发现到上游修复的完整时间线
该问题最初由用户在2026-04-10发现:当时桌宠没开,Edit 调用一直被自动 deny,一开始误以为是 Clawd 自己的 bug。诊断过程还原如下:
- Claude 用
permission-debug.log实证了 CC 给 Write 发 PermissionRequest; - Codex 在 review 中找到了 CC 文档的 non-blocking 承诺;
- Claude WebFetch 核实文档原文(两段 quote);
- 搜索 anthropics/claude-code 仓库 18 个相关搜索词,确认没人报过;
- 用户决定向上游发 issue(anthropics/claude-code#46193),不在 Clawd 侧加 workaround。
随后上游在 v2.1.113 修复,报告者在 v2.1.274 再次验证不再复现,Issue 以 Completed 关闭;2026-09-18 本文档归档为历史记录,调查闭环。
Clawd 侧决策:为什么不做 fail-deny 兜底
调查文档明确列出了一份"不要做的事"清单,Clawd 侧不为这个上游 bug 添加任何 workaround:
- ❌ command hook wrapper(包一层脚本检测端口)
- ❌ quit-time unregister + start-time register(崩溃路径会漏)
- ❌ 强制 auto-start hook 拉起 Electron(用户体验差 + 冷启动竞态)
理由非常务实:这些都是"在帮 CC 擦屁股"。上游修好之后,这些 workaround 会变成废代码 + 维护负债。与其在 Clawd 侧堆砌补偿逻辑,不如通过 issue 推动上游修复——最终上游也确实在 v2.1.113 修复了该问题,验证了这个决策的正确性。
后续收尾事项(CC 修复后)
调查文档同时记录了结案后的收尾清单:
- 监控 anthropics/claude-code#46193 的 status
- CC 修了之后:
- 删除 README.md / README.zh-CN.md 里 Known Limitations 表格的对应行
- 把这个文档归档(移到
docs/archive/或在标题加 RESOLVED) - 顺手更新 hooks/install.js 那条过时注释("Edit/Write permissions are handled by Claude Code's own permission mode — not our hook"),写明历史脉络
从源码看 Clawd 的权限 hook 设计(背景补充)
Clawd 的权限气泡体系以 HTTP hook 为核心:PermissionRequest事件注册到http://127.0.0.1:23333/permission(见 hooks/install.js),服务端在 src/server.js 中处理POST /permission路由,并通过 src/server-route-permission.js 中的handlePermissionPost完成工具名归一化、指纹计算与气泡弹出(对应showing bubble: tool=... session=...日志)。对不支持的 agent 或需要回落的场景,服务端会发送 204 no-decision 响应(如 src/server-route-permission.js 的sendCodexPermissionNoDecision),让 CC/CodeBuddy 回落到原生询问流程——Clawd 的原则是"绝不替用户做 allow/deny 决策"。这一设计原则与"hook 失败应当 non-blocking"的官方语义一致,也正是 fail-deny 问题发生时判定为上游 bug 的根本依据。hook 注册行为在 test/install.test.js 与 test/server-hook-management.test.js 等测试中均有覆盖。
给旧版本用户的排障建议
如果你仍在使用 2.1.100 附近的旧版本 Claude Code 且遇到了"工具调用被自动拒绝"的现象,请按此顺序排查:
- 先确认桌宠是否在运行:
netstat -ano | grep 23333,若 23333 无监听,问题几乎可以锁定为本篇描述的上游 fail-deny 缺陷; - 若桌宠正常开着仍出现拒绝,再检查
settings.json中PermissionRequest段是否被改名/缺失、hook url 是否仍指向http://127.0.0.1:23333/permission; - 终极解法:升级 Claude Code 至 v2.1.113 及以上(已在 v2.1.274 验证修复),此问题在当前版本中不再构成限制。
- 桌面应用
- 交互助手
【免费下载链接】clawd-on-desk
A pixel desktop pet that watches Claude Code, Codex, Cursor & other AI coding agents — so you don't have to.
相关推荐
WSABuilds 故障排查:修复 WSA 端口 58526 连接被拒(错误 10061)的 Hyper-V 端口保留方案
WSABuilds 故障排查:修复 WSA 端口 58526 连接被拒(错误 10061)的 Hyper V 端口保留方案 本文基于 WSABuilds 仓库中
开发工具BullMQ Redis 断连快速失败(Fail Fast)实践:用 enableOfflineQueue 让 HTTP 调用不再被挂起
BullMQ Redis 断连快速失败(Fail Fast)实践:用 enableOfflineQueue 让 HTTP 调用不再被挂起 导读 BullMQ 默
后端消息队列任务调度WSABuilds:Install.ps1 无响应或被拒绝时的 WSA 安装故障排查与手动修复指南
WSABuilds:Install.ps1 无响应或被拒绝时的 WSA 安装故障排查与手动修复指南 本文聚焦 WSABuilds(基于 MagiskOnWSA
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考