claude-code-long-running-process-hang-fix と検索してこのページに辿り着いた方は、おそらく Claude Code に「開発サーバーを起動して動作確認して」と頼んだら、画面がくるくる回ったまま何も返ってこなくなった経験をされているかと思います。
私自身、2014年から個人でアプリを開発してきて、Claude Code をワークフローに組み込み始めた当初にこの問題で何度か時間を溶かしました。npm run dev を Bash ツールで実行させた瞬間、セッションが完全にフリーズするあの感覚は、最初は何が起きているのかまったく分かりませんでした。
なぜ Claude Code の Bash ツールは長時間プロセスでフリーズするのか
Claude Code の Bash ツールは、コマンドの完了を待ってから結果を返す設計になっています。ls、git status、npm install のような「実行して終わる」コマンドには問題ありません。
しかし npm run dev や docker run、python -m http.server のようなフォアグラウンドで動き続けるプロセスを渡すと、Bash ツールはプロセスが終了するまでレスポンスを待ち続けます。開発サーバーは手動で止めない限り終了しませんから、事実上セッションは永遠に応答待ちになります。
これはバグではなく仕様です。「コマンドを実行して結果を受け取る」という設計原則に従った動作ですが、Web 開発では常駐プロセスを扱う場面が多いため、知らないとはまりやすい落とし穴になっています。
症状の確認 — どのコマンドでハングが起きるか
ハングが起きやすいコマンドのパターンです。
フロントエンド / Node.js 系:
npm run dev、yarn dev、pnpm devnext dev、vite、webpack servenpm start(Express サーバー等)
バックエンド・インフラ系:
docker run、docker compose up(-dなし)python manage.py runserverrails server、php artisan serve
ウォッチモード系:
jest --watch、vitest watchtsc --watchnodemon server.js
逆に、一度実行して終わるコマンド(npm run build、docker build、jest --runInBand)はそのまま使えます。
解決策1 — バックグラウンド実行(& + リダイレクト)
最もシンプルな解決策は、コマンドをバックグラウンドで実行させることです。
# 開発サーバーをバックグラウンドで起動し、ログをファイルに保存
npm run dev > /tmp/dev-server.log 2>&1 &
echo "PID: $!"
# 数秒待ってからログを確認
sleep 3 && cat /tmp/dev-server.log& をコマンド末尾に付けると、プロセスをバックグラウンドに送りつつ Bash ツールはすぐに PID を返して終了します。$! で直前に起動したプロセスの PID を取得しておくと、後で停止させるときに便利です。
Claude Code にこのパターンを伝えるプロンプトの例です。
開発サーバーを起動して、3000番ポートで動いているか確認してください。
サーバーはバックグラウンドで起動し、ログは /tmp/dev-server.log に保存してください。
起動確認は curl http://localhost:3000 で行ってください。
解決策2 — timeout コマンドで強制終了タイミングを指定する
「起動して特定のエラーが出ないか確認したい」という場面では、timeout コマンドが役立ちます。
# 10秒間だけ起動してログを取る
timeout 10 npm run dev > /tmp/dev-server.log 2>&1 || true
# ログを確認
grep -E "(error|Error|started|listening|ready)" /tmp/dev-server.logtimeout 10 を付けると、10秒後にプロセスが自動停止します。起動直後のエラーチェックや、ポート待ち受けの確認程度なら十分です。
解決策3 — curl や wait-on で起動完了を確認する
開発サーバーを起動してから「ページが表示されるか確認して」という用途には、次のパターンが実用的です。
# バックグラウンドで起動
npm run dev > /tmp/dev-server.log 2>&1 &
DEV_PID=$!
# ポートが開くまで最大30秒待つ
for i in $(seq 1 30); do
if curl -s http://localhost:3000 > /dev/null 2>&1; then
echo "✅ 起動確認: http://localhost:3000 で応答あり"
break
fi
sleep 1
done
# サーバーを停止
kill $DEV_PID 2>/dev/nullnpx wait-on を使うとより簡潔になります。
npm run dev > /tmp/dev-server.log 2>&1 &
DEV_PID=$!
npx wait-on http://localhost:3000 --timeout 30000 && echo "✅ Server ready"
kill $DEV_PID 2>/dev/null解決策4 — Docker は必ず -d フラグを付ける
Docker コンテナの起動は -d(デタッチ)フラグが必須です。
# ❌ これは止まる
docker run -p 3000:3000 myapp
# ✅ これは動く
docker run -d -p 3000:3000 --name myapp myapp
docker logs myappdocker compose up の場合も同様です。
# ✅ バックグラウンド起動
docker compose up -d
# コンテナの状態確認
docker compose ps
docker compose logs --tail=20Claude Code への指示例です。
docker compose up -d で起動して、すべてのサービスが healthy になっているか確認してください。
確認後に docker compose ps の結果を教えてください。
CLAUDE.md にプロセス管理の規則を書いておく
このパターンを毎回プロンプトで説明するのは手間なので、プロジェクトの CLAUDE.md に一度書いておくと楽です。
## プロセス管理ルール
- 開発サーバーの起動は必ずバックグラウンド実行すること
- 例: `npm run dev > /tmp/dev.log 2>&1 &`
- docker compose は必ず `-d` フラグを付ける
- 起動確認は curl または wait-on で行う
- プロセスの停止は `kill <PID>` または `docker compose down`
- テストはウォッチモードではなく `--runInBand` または通常実行を使うClaude Code の CLAUDE.md 設定ガイドでも、このような実行環境の制約をプロジェクトに記述する方法を解説しています。
フリーズしてしまったセッションの回復方法
すでにセッションがフリーズした状態の場合は、以下の手順で回復できます。
1. Claude Code の停止ボタンを押す(ターミナルの場合は Ctrl+C)
進行中のリクエストを中断します。
2. 残ったプロセスを手動で停止する
# ポート3000を使っているプロセスを確認
lsof -ti:3000 | xargs kill 2>/dev/null
# または名前で探す
pkill -f "npm run dev"
pkill -f "vite"3. ポートが解放されたか確認する
lsof -i:3000
# 何も表示されなければポートは空きウォッチモードのテストが止まる問題
jest --watch や vitest watch も同様にハングします。CI 相当の動作をさせる場合は、ウォッチフラグを外してください。
# ❌ ウォッチモードは止まる
jest --watch
# ✅ 一回実行して終了
jest --runInBand
npx vitest run
# カバレッジ付き
jest --coverage「テストが全部通るか確認して」と Claude Code に頼む際は、ウォッチモードを使わない旨を明示すると確実です。
よくある間違いパターン
実際の開発で遭遇しやすい、プロンプトの書き方とその改善例です。
❌ フリーズするプロンプト例:
開発サーバーを起動して、ページが崩れていないか確認してください
Claude Code がそのまま npm run dev を実行してフリーズします。
✅ フリーズしないプロンプト例:
開発サーバーをバックグラウンドで起動(npm run dev > /tmp/dev.log 2>&1 &)して、
起動後に curl http://localhost:3000 でレスポンスを確認してください。
確認が終わったらサーバーを停止してください。
明示的にバックグラウンド実行の指示を入れることで、Claude Code は適切なコマンド設計をしてくれます。
この問題で詰まっている方の参考になれば幸いです。Claude Code の Bash ツールは「コマンドを実行して終わる」設計だということを意識しておくと、他のハング問題(python input() を含むスクリプト、対話型 CLI ツール等)にも同じ考え方で対処できます。
関連するトラブルシューティングとして、Claude Code の Bash ツール実行エラー全般の解決ガイドも参考になります。