Node.js で「Cannot find module ‘node:fs’」エラーの原因と解決策

Node.js のバージョンアップ後、または新しいプロジェクトをビルド・実行した際に、以下のようなエラーが出ることがあります。

Error: Cannot find module 'node:fs'

このエラーは一見「fs モジュールが見つからない」という単純な問題に見えますが、実際には Node.js のモジュール解決方式の変更、および 開発環境や bundler(Webpack、Vite、ESBuild)側の設定不整合 に起因するケースがほとんどです。
特に Node.js v16 以降で導入された node: プレフィックス付きの組み込みモジュール指定 が関係しています。

本記事ではこの問題の背景、発生条件、再現例、そして確実な解決方法を技術者向けに詳しく解説します。

1. エラーの原因:node: プレフィックス導入の経緯

Node.js 16 以降では、内部モジュールをより明示的に区別するために node: プレフィックスが正式導入されました。
たとえば、次の2つのコードは同じ意味を持ちます。

// 従来の書き方(Node 14以前から存在)
import fs from 'fs'

// 新しい書き方(Node 16+ 推奨)
import fs from 'node:fs'

Node.js v16 以降のネイティブ ES Modules (ESM) 環境では、この node: 接頭辞を利用することで、標準モジュールであることを明示 できます。

しかし、古い Node.js 環境(特に v14 以下)や、一部の bundler(Webpack 4, Rollup など)はこの表記を理解できず、「モジュールが存在しない」と誤認して Cannot find module 'node:fs' エラーを発生させます。

2. 再現例:環境差によるエラー発生パターン

次のようなコードがあるとします。

import fs from 'node:fs'

const data = fs.readFileSync('./data.json', 'utf-8')
console.log(data)

これを Node.js 14Webpack 4 ベースの環境 で実行すると、以下のエラーが発生します。

Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'node:fs'

一方で、同じコードを Node.js 18 以降で動かすと、正常に動作します。

このように、Node バージョン差・ビルド環境差・ESM/CJS の混在が絡むと、開発者が意図していないエラーが起きるのです。

3. 対策1:環境に合わせた fs モジュールの指定

もっともシンプルな対処は、環境に応じて node: プレフィックスの有無を切り替えることです。

✅ Node.js 16 以上の場合(ESM対応)

import fs from 'node:fs'

✅ Node.js 14 以下や CJS の場合

const fs = require('fs')

または、ESM でも互換性を保つために createRequire を利用する方法もあります。

import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const fs = require('fs')

この方法なら、Node.js 14 以前の環境でも ESM 構文を維持しつつ fs を読み込むことができます。

4. 対策2:Bundler 設定の調整

Vite、Webpack、Rollup などのフロントエンドビルド環境を使う場合も、node: プレフィックスの解決ができずにエラーが出ることがあります。

Webpack 5 以前

Webpack 4 系では node: の解決がサポートされていません。
そのため、resolve.fallback を設定して明示的にマッピングする必要があります。

// webpack.config.js
module.exports = {
  resolve: {
    alias: {
      'node:fs': 'fs'
    },
    fallback: {
      fs: false // フロントでは fs が使えない場合の設定
    }
  }
}

Vite / Rollup

Vite は Node.js 標準モジュールをそのまま使う場合に define: { global: 'globalThis' } の設定や polyfill を追加する必要があるケースがあります。

// vite.config.js
export default {
  resolve: {
    alias: {
      'node:fs': 'fs'
    }
  },
  define: {
    'process.env': {}
  }
}

ブラウザ側では fs は動作しないため、Node 環境でのみ実行されるようにコード分離(Dynamic Import や server-only フラグ)を行いましょう。

5. 対策3:Node バージョンの固定・更新

開発メンバーや CI/CD 環境で Node.js のバージョンがバラバラだと、この問題が頻発します。
特に LTS バージョン間(v14 ↔ v18)では、モジュール解決ルールが異なるため、バージョン固定は必須です。

推奨対応

  • .nvmrc をプロジェクトルートに配置し、バージョンを固定
  • package.json"engines": { "node": ">=18" } を追記
  • Dockerfile でも同一 Node バージョンを使用
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm ci
CMD ["npm", "start"]

このように 環境全体を Node.js 18+ に統一する ことで、node: 接頭辞を正式に活用でき、将来的な互換性も確保できます。

6. 実務的なチェックポイントとベストプラクティス

最後に、チーム開発・本番環境でこの問題を回避するための実践的なポイントを整理します。

  • Node.js バージョン統一
    nvm、Volta、Docker、CI設定で Node 18+ に統一する
  • ESM/CJS の混在を避ける
    "type": "module" を使うなら、すべてのファイルで import 構文を採用する
  • node: 接頭辞は安全に使う
    Node 18 以上なら推奨。旧環境では fs など従来の書き方を維持
  • Bundler のバージョン確認
    Webpack 5, Vite 4 以降なら対応済み。古い設定では alias 必須
  • 依存パッケージ内の node: 記述に注意
    サードパーティ製ライブラリが node:cryptonode:url を import していると、古い bundler 環境で再現性のないビルドエラーが起こる

デバッグのコツ

# どのモジュールが node:fs を要求しているか調査
grep -R "node:fs" node_modules/

エラー発生元のライブラリを特定し、バージョンアップまたは alias 設定で回避します。

まとめ

「Cannot find module ‘node:fs’」エラーは、Node.js 16 以降の node: プレフィックス導入による互換性問題が原因です。
解決策としては、環境を Node 18+ に統一するか、古い環境では従来の fs import を利用するのが最も確実です。
Bundler を使う場合は alias 設定を追加して互換性を保ちましょう。
また、プロジェクト全体で Node バージョンを統一することが、将来的なトラブル回避につながります。

参考

タイトルとURLをコピーしました