Claude Code 工具执行卡死无超时

Bash 命令永远跑、agent 永远等——没设超时一个挂死的进程就锁住整个 session。设超时、识别 hang、干净地 kill。

Claude Code 跑了 npm run dev,等啊等啊等。dev server 起来了一直跑着不退——agent 以为命令”还没完”、session 转着圈卡住。或者 Bash(curl https://flaky-api.com) 卡在 TCP 读,因为对端从来不关连接。或者 Python 脚本进了死循环、模型耐心等 stdout 等好几小时。session 活着但没用。Claude Code 的 Bash 工具有默认超时、但很多现实命令要么比默认更长、要么本来就该一直跑——agent 没有内建办法区分”还在工作”和”永远卡住”。修法是显式设超时、用对执行模式(前台 vs 后台)、在 prompt 里加 hang 检测模式。

常见原因

1. 长跑命令在前台跑、没超时

npm run devnext devpython -m http.servertail -f——这些设计就是一直跑、只能 Ctrl-C 才停。在 Bash 工具里前台跑会撞默认超时(通常 2 分钟)、然后 agent 一头雾水。

如何判断:卡的命令是 server 或 watcher。终端输出 “Server running on…” 之类、然后没声音了。

2. 默认 2 分钟超时对任务太短

真长但有界的任务(测试套件、大构建、800 个包的 npm install)合理需要 5 分钟以上。工具超时、agent 以为失败了、重试、再吃更多时间。

如何判断:任务确实长但有上界。工具失败信息是 timeout、不是命令报错。

3. 网络调用打到一个不响应的端点

curlwgetgit fetch 打到一个接受连接但永远不发数据的端点。TCP socket 开着、工具一直等。

如何判断:命令是网络相关的。没有进度输出、没有报错。kill 重试经常就好。

4. 命令在等 stdin

有些命令没被 pipe 时会从 stdin 读:rg --interactive、不带 -mgit commit、REPL 启动、shell 里调 read 的任何东西。工具没有终端可以读。

如何判断:这条命令在真终端里会向人提示。没有输出、无限等待。

5. 进程派生了一个 daemon、detach 了

不带 -ddocker compose up、触发 postinstall daemon 的 pnpm install、fork 到后台但父进程还活着在 pipe 上等的脚本。

如何判断:开头有点输出然后卡住了、虽然”看起来工作做完了”。多半是子进程把父进程吊在那里。

6. hook 包裹了命令、阻塞了

PreToolUsePostToolUse 里一个会卡的 hook 会无限阻塞工具调用。工具本身瞬间跑完、hook 在卡。

如何判断:工具调用开始了但真正命令没有任何输出。装过自定义 hook 的话先怀疑这个。

动手前先确认

  • 区分 Claude Code 是真卡了还是只是在跑一个合法的慢任务。
  • 看清到底哪条命令卡了——server / 测试 / 网络调用的恢复路径不同。
  • 能保留 session 就保留——杀掉会丢失能帮判断原因的上下文。

需要收集的信息

  • 卡住的 Bash 命令原文、含任何包装。
  • 进入沉默前打过的输出。
  • 你这个版本 Claude Code 的 Bash 默认超时。
  • 同样命令在普通终端里跑能不能完。
  • ~/.claude/settings.json 或项目 settings 里有没有自定义 hook。
  • 卡的时候的进程树:另开终端 ps -ef | grep <command>

最短修复路径

Step 1:先判断这命令到底会不会结束

三类、三种修法:

类别例子修法
有界(会结束)测试、构建、安装加超时
无界 serverdev server、watcher、daemon放后台
卡死等网络挂了、stdin 读kill 掉、避免再这么调

先分类对、再选修法。

Step 2:有界长任务、显式设超时

用 Bash 工具的 timeout 参数(如果支持),或者包一层:

# 通过工具参数显式超时(推荐)
Bash(npm test, timeout=600000)  # 10 分钟、毫秒

# 或者 shell 里包 timeout
Bash(timeout 600 npm test)

超时设到预期运行时长的 2-3 倍、合法的慢跑不会被误杀。

Step 3:server 和 watcher 用后台执行

后台跑、输出捕获到日志:

# dev server 后台启动、日志写文件
Bash(npm run dev > /tmp/dev-server.log 2>&1 &)

# 短暂等启动
Bash(sleep 3 && curl -s localhost:3000 | head -5)

# 之后看进度
Bash(tail -20 /tmp/dev-server.log)

agent 立刻放出来、之后想看输出再看。

Step 4:加 hang 检测 prompt

教 Claude 识别 hang 并自救。加进 CLAUDE.md 或 session prompt:

对任何 Bash 命令:60 秒没输出、且这条命令不是已知的长跑型,那就:
1. 停下来告知用户
2. 建议 kill 进程换条路
3. 不要静默重试同一个卡住的命令

长任务(测试、构建)一律给 Bash 显式 timeout。
server 和 watcher 一律放后台、输出写 /tmp/。

模型会尊重这条指引、早早暴露 hang。

Step 5:干净地 kill 掉卡住的进程

卡住时不丢 session 地恢复:

# 另开一个终端:
ps -ef | grep <command>
kill -TERM <pid>
# TERM 不行就:
kill -KILL <pid>

# 然后 Claude Code 里中断工具调用:
# 按 Esc / Ctrl-C、看你的客户端

杀完让 Claude 确认这次失败、改走别的路。

Step 6:怀疑 hook 阻塞就验证一下

怀疑 hook 在卡就临时禁用:mv ~/.claude/settings.json ~/.claude/settings.json.bak,重启 Claude Code,重试命令。不带 hook 文件能跑就是 hook 的问题——恢复回来、把 hook 的 matcher 收窄、不要在无关命令上跑。

怎么确认已经修好

  • 原本卡住的命令现在要么在超时内完成、要么后台跑放回控制权、要么被识别为卡死并报给你。
  • Claude Code 不再静默重试卡住的命令。
  • 长跑 server 的输出捕获在 /tmp/ 里、你能 tail。
  • server 后台跑的时候 session 还能干别的活。
  • CLAUDE.md 里的 hang 检测指引让 Claude 60 秒没输出就早早 bail。

长期预防

  • 任何预计 >30 秒的命令都给 Bash 显式 timeout。
  • 跑 server、watcher、daemon 的命令一律默认后台。
  • package.json scripts 里建一组安全包装、打印进度、自终止。
  • hang 检测 prompt 加到 CLAUDE.md、每个 session 都继承。
  • agent 上下文里别用读 stdin 的命令——参数靠 flag 传。
  • hook 尽量少、matcher 尽量窄、不要意外阻塞无关工作。
  • 网络调用一律带 --max-time 或等价超时 flag。

常见坑

  • 让 agent 把同一个卡住的命令重试 3 次——浪费上下文和时间、永远不会恢复。
  • 超时设太紧(60 秒)卡住合法长任务——假 hang 和真 hang 一样糟。
  • 前台跑 npm run dev 然后惊讶 agent 锁住了。
  • 忘了清理后台进程——电脑上最后留着 12 个泄漏的 dev server。
  • 在工具调用里用 tail -f”看实时进度”——这等于自找 hang。

FAQ

Q:Claude Code 的 Bash 默认超时是多少? A:前台执行通常 2 分钟(120000 ms)。显式传 timeout 参数能盖过去、最大到大约 10 分钟。

Q:后台进程还在跑、Claude Code 看得到吗? A:直接看不到——后台之后就是普通 OS 进程。Claude 可以读日志文件或者跑 ps / curl localhost:PORT 来查状态。

Q:长跑命令是不是都该放后台? A:server 和 watcher 该。有限任务(测试、构建)不该——前台跑加大超时、agent 才能看到 exit code。

Q:要交互输入(密码、确认)怎么办? A:agent 上下文里别用交互命令。用 flag(--yes--no-input)或者把凭据放环境变量 / 配置文件。git commit -m "msg" 而不是 git commit

相关阅读

标签: #Claude Code #agent #排查