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 dev、next dev、python -m http.server、tail -f——这些设计就是一直跑、只能 Ctrl-C 才停。在 Bash 工具里前台跑会撞默认超时(通常 2 分钟)、然后 agent 一头雾水。
如何判断:卡的命令是 server 或 watcher。终端输出 “Server running on…” 之类、然后没声音了。
2. 默认 2 分钟超时对任务太短
真长但有界的任务(测试套件、大构建、800 个包的 npm install)合理需要 5 分钟以上。工具超时、agent 以为失败了、重试、再吃更多时间。
如何判断:任务确实长但有上界。工具失败信息是 timeout、不是命令报错。
3. 网络调用打到一个不响应的端点
curl、wget、git fetch 打到一个接受连接但永远不发数据的端点。TCP socket 开着、工具一直等。
如何判断:命令是网络相关的。没有进度输出、没有报错。kill 重试经常就好。
4. 命令在等 stdin
有些命令没被 pipe 时会从 stdin 读:rg --interactive、不带 -m 的 git commit、REPL 启动、shell 里调 read 的任何东西。工具没有终端可以读。
如何判断:这条命令在真终端里会向人提示。没有输出、无限等待。
5. 进程派生了一个 daemon、detach 了
不带 -d 的 docker compose up、触发 postinstall daemon 的 pnpm install、fork 到后台但父进程还活着在 pipe 上等的脚本。
如何判断:开头有点输出然后卡住了、虽然”看起来工作做完了”。多半是子进程把父进程吊在那里。
6. hook 包裹了命令、阻塞了
PreToolUse 或 PostToolUse 里一个会卡的 hook 会无限阻塞工具调用。工具本身瞬间跑完、hook 在卡。
如何判断:工具调用开始了但真正命令没有任何输出。装过自定义 hook 的话先怀疑这个。
动手前先确认
- 区分 Claude Code 是真卡了还是只是在跑一个合法的慢任务。
- 看清到底哪条命令卡了——server / 测试 / 网络调用的恢复路径不同。
- 能保留 session 就保留——杀掉会丢失能帮判断原因的上下文。
需要收集的信息
- 卡住的
Bash命令原文、含任何包装。 - 进入沉默前打过的输出。
- 你这个版本 Claude Code 的 Bash 默认超时。
- 同样命令在普通终端里跑能不能完。
~/.claude/settings.json或项目 settings 里有没有自定义 hook。- 卡的时候的进程树:另开终端
ps -ef | grep <command>。
最短修复路径
Step 1:先判断这命令到底会不会结束
三类、三种修法:
| 类别 | 例子 | 修法 |
|---|---|---|
| 有界(会结束) | 测试、构建、安装 | 加超时 |
| 无界 server | dev 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.jsonscripts 里建一组安全包装、打印进度、自终止。 - 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 #排查