next build は通るのに next dev だけ壊れる:.next のキャッシュが原因の3パターン
全ルートが404になる。Turbopack が FATAL で起動しない。消すと直るのにまた再発する。どれもコードではなく .next 配下の状態が原因で、症状の形から見分けられる。
開発サーバーだけが死ぬ。next build は通る。tsc も通る。コードは触っていない。この形になったら、犯人はだいたい .next(Next.js がビルド生成物とキャッシュを置くディレクトリ)だ。踏んだ3パターンと、症状からの見分け方。
パターン1:build の直後に dev を起動すると、全ルートが404になる
事象
同じプロジェクトで一度 next build を流し、そのあと next dev を起動した。トップページを含む全ルートが404を返す。
厄介なのは、壊れているように見えないところだ。dev サーバー自体は起動して応答する。ターミナルにエラーも警告も出ない。アプリ側のコードも実行される。返るのが404なだけ。
原因
next build と next dev は同じ .next ディレクトリを使う。build が書いた本番用のマニフェスト(どのルートがどのファイルに対応するかの索引)が残ったところへ dev が起動すると、ルートの解決に失敗して404を返すことがある。dev の内部で何が起きているかまでは追っていない。ただ .next を消せば必ず直るので、原因がそこに残った状態にあることは確かだ。
CIやスクリプトで「ビルドが通るか確認してから開発に戻る」というワークフローを組んでいると踏みやすい。確認のつもりで流した build が、次の dev を壊す。
解決策
.next を消してから起動し直す。
rm -rf .next && npm run dev
これで dev 専用の .next が作り直される。build 検証をスクリプトに組み込んでいるなら、検証の後始末として rm -rf .next を入れておくと再発しない。
逆向き(dev を起動したまま build を流す)でも壊れるが、そちらは500と ENOENT で派手に落ちるので気づきやすい。別記事「next dev を動かしたまま next build すると dev サーバーが壊れる」に書いた。
パターン2:Turbopack の永続キャッシュが壊れて dev が起動しない
事象
next dev が「Ready」と出た直後に落ちる。curl で叩くと接続拒否になる。
FATAL: An unexpected Turbopack error occurred
thread '...' panicked at ...: Failed to restore task data (corrupted database or bug)
Unable to open static sorted file .../00000001.sst: No such file or directory
別の日には、こういう出方もした。
Failed to open database
Loading persistence directory failed
invalid digit found in string
文言は違うが、.sst ・ task data ・ restore ・ persistence あたりの語が並ぶのは共通していた。
原因
Turbopack は差分ビルドを速くするために、.next 配下へディスク上のデータベースとしてキャッシュを持つ。そこが壊れると起動時の読み込みで死ぬ。
コード側は何も悪くない。証拠に next build は通る。本番ビルドは別経路を通るので、dev のキャッシュ破損に巻き込まれない。この非対称が、原因をコードから引き剥がす手がかりになる。
解決策
同じく .next を消す。
rm -rf .next && npm run dev
キャッシュなので、消して困るものは入っていない。次回の起動が少し遅くなるだけだ。
パターン3:消すと直るのに、また出る
事象
パターン2を rm -rf .next で直した。数日後にまた同じエラーが出る。また消す。また出る。
原因
プロジェクトの置き場所を疑う。macOS で「デスクトップと書類フォルダ」を iCloud Drive に同期する設定を有効にしていると、~/Desktop や ~/Documents の下に置いたプロジェクトは、.next も含めて同期対象になる。
ここでストレージの最適化が働くと、しばらく使われていないファイルの実体をクラウドへ退避する。ローカルにはファイル名だけが残り、中身が無い状態(dataless)になる。Turbopack のキャッシュ実体がこれをやられると、次の起動で「あるはずのファイルが開けない」になる。エラーが No such file or directory と言っているのは、文字どおりの意味だった。
再生成できるビルド生成物が同期対象に入っていること自体が、そもそも噛み合っていない。
解決策
本命は、プロジェクトを iCloud の同期範囲の外へ出すこと。~/dev/ のようなホーム直下の別ディレクトリに移せば、この種の再発はまとめて消える。
すぐ移せない場合の暫定策は2つ。
- Finder でプロジェクトフォルダを右クリックし「このMacにダウンロードしたままにする」を選ぶ(退避されなくなる)
- システム設定の iCloud で「Macのストレージを最適化」を切る
.next だけを同期対象から外すオプションは無い。フォルダ名の末尾に .nosync を付けて除外する手はあるが、.next という名前は Next.js が決めるので、この方法は使えない。
同じ理由で、iCloud 配下ではネイティブビルドや git のオブジェクトも壊れることがある。iOS のアーカイブで踏んだケースは「iCloud同期フォルダ配下でネイティブarchiveが壊れる」に書いた。原因は同じで、退避されて困るものを同期の下に置いている。
見分け方
| 症状 | まず見るところ |
|---|---|
| 全ルート404・エラーも警告も無し | 直前に next build を流していないか |
.sst / task data / persistence を含む致命エラー | .next のキャッシュ破損 |
rm -rf .next で直るが繰り返す | プロジェクトの置き場所がクラウド同期の下でないか |
効くパターン
3つに共通しているのは、next build と tsc がクリーンなのに next dev だけが落ちる、という非対称だった。この形を見たら、コードを読む前に .next を消す。1コマンドで、コード側の疑いを外せる。
順番を逆にすると高くつく。ソースを疑って調べ始めると、直っていないのに「たぶんこれ」という当たりを何個も作ってしまう。先に消せば、消しても直らなかったという事実が残るだけで済む。