~/gtn74

$ cat posts/20260711-9zsc1x.mdx

Cannot find module './611.js' の正体 — Next.jsの.nextキャッシュとブランチ切り替え

2026-07-11 · #tech #nextjs

はじめに

このブログの開発中に、devサーバーでページを開いたら突然こんなエラーが出た。

Runtime Error

Cannot find module './611.js'
Require stack:
- .next/server/webpack-runtime.js
- .next/server/app/blog/[slug]/page.js
- ...

611.js なんてファイル、自分では一度も書いていない。コードのどこを見ても原因が見つからない。

結論から書くと、コードのバグではなく .next ディレクトリ(ビルドキャッシュ)とソースコードの不整合だった。直し方はこれだけ。

# devサーバーを止めてから
rm -rf .next
npm run dev

この記事では「なぜこれで直るのか」を、.next に何が入っているのかから追ってみる。

何をしたら壊れたのか

状況はこうだった。

  1. npm run dev でdevサーバーを起動したまま
  2. 別の作業のために git checkout でブランチを何度も切り替えた
  3. ブラウザでページを開いたら冒頭のエラー

devサーバーを起動しっぱなしで、その下のソースコードをブランチ切り替えで丸ごと入れ替えたのが原因だ。

.next には何が入っているのか

next devnext build を実行すると、プロジェクト直下に .next ディレクトリができる。中身はだいたいこう。

.next/
├── cache/          # webpack/SWCのコンパイルキャッシュ(ビルド高速化用)
├── server/         # サーバー側で実行されるコンパイル済みJS
│   ├── webpack-runtime.js   # ← エラーのRequire stackの先頭にいたやつ
│   └── app/blog/[slug]/page.js
└── static/         # ブラウザに配信されるJS/CSS(チャンク)

ポイントは、webpackがコードを数値IDのチャンクに分割することだ。611.js というのはwebpackが機械的に採番したチャンクファイルで、webpack-runtime.js が「モジュールID → チャンクファイル」の対応表を持っている。

なぜブランチ切り替えで壊れるのか

devサーバーのコンパイルは差分(インクリメンタル)前提で動いている。ファイルを1つ保存したら、その差分だけを再コンパイルして、既存のチャンクやID対応表は再利用する。

ところが git checkout はファイルを丸ごと入れ替える。モジュールの依存グラフが大きく変わると、チャンクの採番もやり直しになる。このとき:

  • 対応表(webpack-runtime.js)は「611.js にあるはず」と古い情報を持ったまま
  • 実際の 611.js は再採番で消えている

という食い違いが起きて、Cannot find module './611.js' になる。

.next を消せば対応表もチャンクもゼロから作り直されるので、確実に直るというわけだ。

もうひとつの引き金: npm installvendor-chunks/*.js

後日、今度は別のブランチで作業中にこんなエラーが出た。

Cannot find module './vendor-chunks/esprima.js'
Require stack:
- .next/server/webpack-runtime.js
- .next/server/app/blog/[slug]/page.js
- ...

ファイル名は 611.js と違うが、Require stackの顔ぶれは同じ。これも .next とソースの不整合で、rm -rf .next で直った。前と違うのは2点だけだ。

引き金が git checkout ではなく npm install だった。 このときは新しい依存(ESLint)を足した直後だった。npm installnode_modules の中身を書き換えるので、ブランチ切り替えと同じく依存グラフが変わり、チャンクの採番がやり直しになる。自分のソースを1行も触っていなくても、依存を足しただけで壊れることがある、というのがポイント。

チャンク名が数字ではなく vendor-chunks/<名前>.js だった。 webpackは自分のアプリコードを数値ID(611.js)に、node_modules 由来のコードを vendor-chunks/<パッケージ名>.js に分けて出力する。今回の esprima は、frontmatterをパースする gray-matter が内部で使っている依存だ(gray-matter → js-yaml → esprima)。数字でも名前でも、エラーの本質は同じ「対応表とチャンクの食い違い」。

まとめると、.next が壊れる引き金は「ソースの入れ替え(git checkout)」だけでなく「依存の入れ替え(npm install」もある。どちらも対処は同じ rm -rf .next だ。

副作用: エラーメッセージが当てにならなくなる

キャッシュ不整合の厄介なところは、表示されるエラーが本当の原因と無関係なことだ。今回の調査中には、同じ原因でこんなエラーも出た。

[Error: <Html> should not be imported outside of pages/_document.]
Error occurred prerendering page "/404".

App Routerしか使っていないのに pages/_document の話をされて面食らったが、これも rm -rf .next で消えた。「コードを触っていないのに意味不明なエラーが出た」ときは、まずキャッシュを疑うのが精神衛生上よい。

対処と予防

直すとき

# devサーバーを Ctrl+C で止める(起動したまま消すと再生成と競合する)
rm -rf .next
npm run dev

.next/cache が消えるので初回コンパイルは少し遅くなるが、それだけ。.next はいつ消しても安全な純粋な生成物だ(gitignore対象でもある)。

予防するには

  • ブランチを切り替えたら、devサーバーを再起動する癖をつける。大きく履歴が飛ぶとき(rebase後、複数ブランチ間の移動)は .next も消す
  • npm install で依存を足した/変えた直後も同じ。dev起動中なら再起動、怪しければ rm -rf .next
  • レビューなどで複数ブランチを行き来するなら git worktree でブランチごとに作業ディレクトリを分けると、.next もディレクトリごとに分離されて衝突しない
  • CI(このブログはGitHub Actions)では毎回クリーンな環境でビルドするので、この問題はローカル固有

まとめ

  • Cannot find module './<数字>.js''./vendor-chunks/<名前>.js' も、正体は .next とソースの不整合。コードは悪くない(数字=アプリコード、名前=node_modules由来のチャンク)
  • devサーバーは差分コンパイル前提なので、git checkout(ソースの入れ替え)にも npm install(依存の入れ替え)にも弱い
  • 直すのは rm -rf .next、予防は「ブランチ切り替え・依存追加のあとにdev再起動」か git worktree
  • キャッシュが壊れているときのエラーメッセージは信用しない

$