最終更新:

【Next.js】TurbopackでCannot find module as expression is too dynamic


こんにちは、フリーランスエンジニアの太田雅昭です。

本日は、Next.js 16のTurbopackを使用した際に遭遇した、分かりづらいエラーとその解決方法について紹介します。

環境

  • パッケージマネージャー: pnpm
  • 依存パッケージ
    • next: 16.1.1 (Turbopack)
    • @google-cloud/tasks: 6.2.1

問題

Next.js 16のTurbopackを使用してビルドを実行したところ、以下のエラーが発生しました。

Error: Cannot find module as expression is too dynamic

直訳すると「式が動的すぎるためモジュールが見つかりません」となります。動的インポートの記述が複雑すぎて、Turbopackがモジュールの解決を行えなかったことが原因のようです。

エラーの全文

エラーメッセージの全文を確認すれば、何か手がかりが得られるかもしれません。全文は下記です。

 Next.js 16.1.1 (Turbopack)

  Creating an optimized production build ...
 Compiled successfully in 1762.2ms
 Finished TypeScript in 951.9ms
  Collecting page data using 7 workers  ..Error: Failed to collect configuration for /
    at ignore-listed frames {
  [cause]: Error: Cannot find module as expression is too dynamic
      at <unknown> (.next/server/chunks/ssr/[root-of-the-server]__006d6436._.js:15:287766)
      at c.getJSON (.next/server/chunks/ssr/[root-of-the-server]__006d6436._.js:15:287858)
      at module evaluation (.next/server/chunks/ssr/[root-of-the-server]__006d6436._.js:16:596)
      at instantiateModule (.next/server/chunks/ssr/[turbopack]_runtime.js:740:9)
      at getOrInstantiateModuleFromParent (.next/server/chunks/ssr/[turbopack]_runtime.js:763:12)
      at Context.esmImport [as i] (.next/server/chunks/ssr/[turbopack]_runtime.js:228:20)
      at module evaluation (.next/server/chunks/ssr/_7c907267._.js:1:60)
      at instantiateModule (.next/server/chunks/ssr/[turbopack]_runtime.js:740:9)
      at getOrInstantiateModuleFromParent (.next/server/chunks/ssr/[turbopack]_runtime.js:763:12)
      at Context.commonJsRequire [as r] (.next/server/chunks/ssr/[turbopack]_runtime.js:249:12) {
    code: 'MODULE_NOT_FOUND'
  }
}

> Build error occurred
Error: Failed to collect page data for /
    at ignore-listed frames {
  type: 'Error'
}

エラーの全文を確認しても、具体的にどのファイルのどの箇所が問題なのか判然としません。スタックトレースはすべてビルド後の.nextディレクトリ内のファイルを指しており、元のソースコードとの対応が取れない状況でした。

AIに相談しても、見当違いのライブラリを指摘されるなど、全く解決の糸口が見えません。せめてエラーメッセージで問題箇所を特定できるようにしてほしいというのは、開発者としての切実な願いです。

原因を探す

エラーメッセージからは原因を特定できなかったため、地道な調査を行うことにしました。

  1. コードのコメントアウト: 疑わしい箇所のコードを段階的にコメントアウトし、エラーが消えるポイントを探す
  2. 専用のエクスポートファイルの作成: 外部ライブラリのインポートを分離し、問題の所在を明確にする

このような試行錯誤の結果、最終的に@google-cloud/tasksパッケージを使用している箇所でエラーが発生していることが判明しました。

解決法

原因が@google-cloud/tasksパッケージであることが判明したため、このパッケージをサーバーサイドの外部パッケージとして明示的に指定します。

Next.jsの設定ファイルにserverExternalPackagesオプションを追加し、Turbopackがこのパッケージをバンドルせず、Node.jsのモジュール解決機構に任せるようにします。

// next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  serverExternalPackages: ["@google-cloud/tasks"],
};

export default nextConfig;

この設定により、@google-cloud/tasksはバンドル対象から除外され、実行時にNode.jsによって直接読み込まれるようになります。

これでビルドが正常に完了するようになりました。

Cloud Runでの問題

上記のようにserverExternalPackagesに指定した関係で、サーバーレスであるCloud Runでエラーが発生しました。

⨯ Error: Failed to load external module @google-cloud/tasks-045a0061fd26c043: Error: Cannot find module '/app/node_modules/.pnpm/@google-cloud+tasks@6.2.1/node_modules/@google-cloud/tasks/build/protos/protos.json'

Next.jsをstandaloneでビルドしているのですが、Docker内に適切に必要なファイルを指定できていなかったためでした。以下のようにcloud-tasksで必要なライブラリを含めることで解決しました。なお下記はdaggerのコードの一部です。

.withDirectory("/app/node_modules/.pnpm", deps.directory("/app/node_modules/.pnpm"), {
  include: [
    "@google-cloud+tasks@*/**",
    "@grpc+grpc-js@*/**",
    "google-gax@*/**",
    "google-auth-library@*/**",
    "protobufjs@*/**",
    "long@*/**",
  ],
})

今回は試していませんが、もっと簡単にするには下記のようにincludeを省略すればいいようです(Claude談)。ただし容量が増えるため、できるだけ調整はしたほうがいいかと思います。ライブラリのバージョンアップなどで発狂する前に使うのが良さそうですね。

.withDirectory("/app/node_modules/.pnpm", deps.directory("/app/node_modules/.pnpm"))

まとめ

Turbopackでエラーになるライブラリは、serverExternalPackagesに指定すれば解決する場合があります。今回はコードを直接コメントアウトしたりして原因調査を行いましたが、そうではなく初めから怪しいライブラリを全てserverExternalPackagesに指定して、テストしながら減らす方法も有効ですね。