Node.js で「Error: spawn ENOENT」エラーが出る原因と対処法

Node.js で child_process.spawnexecFile を使って外部コマンドを実行するとき、環境によって頻繁に遭遇するのが「Error: spawn ENOENT」です。
特に Mac・Windows・Docker など環境が異なる場合や、パス設定・実行権限に問題がある場合に発生しやすいエラーです。

この記事では、その原因と代表的な解決策をわかりやすく整理します。

エラーの意味

ENOENT は “Error NO ENTry” の略で、「指定されたファイルやコマンドが見つからない」という意味です。
Node.js の spawn 関数が呼ばれる際、内部で OS にコマンドを実行させようとしますが、そのコマンドのパスが解決できなかった場合にこのエラーが発生します。

Error: spawn someCommand ENOENT

つまり、Node.js 自体が壊れているのではなく、「呼び出そうとしたプログラムが存在しない、または実行できない」ことを示しています。

よくある原因

1. 実行ファイルのパスが通っていない

もっとも一般的な原因です。
spawn はシェルを経由せずに直接プロセスを立ち上げるため、コマンドが PATH 環境変数で解決できないと失敗します。

const { spawn } = require('child_process')

spawn('python', ['script.py']) // ← python が PATH にないと ENOENT

例えば、Node.js 実行時の環境に Python がインストールされていない、または PATH が設定されていない場合にこのエラーが出ます。

対策:

  • 絶対パスを指定する spawn('/usr/local/bin/python3', ['script.py'])
  • もしくは env オプションで PATH を明示的に渡す spawn('python3', ['script.py'], { env: { ...process.env, PATH: '/usr/local/bin:' + process.env.PATH } })

2. Windows 特有の拡張子解決の違い

Windows では、実行ファイルに .exe, .bat, .cmd などの拡張子が必要ですが、spawn はそれを自動的に解決しません。
次のように書くと ENOENT が出やすくなります。

spawn('npm', ['run', 'build']) // ❌ ENOENT: npm.cmd が実体

対策:

  • shell: true を指定してシェル経由で実行する spawn('npm', ['run', 'build'], { shell: true })
  • または、Windows 専用で拡張子を含める spawn('npm.cmd', ['run', 'build'])

3. 実行権限がない

Mac や Linux では、対象ファイルに実行権限 (chmod +x) がないと ENOENT が出る場合があります。

chmod +x script.sh

また、リポジトリをクローンしたあとに権限が落ちていることもあるため、CI/CD や Docker 内で再現するケースもあります。

4. Docker や CI 環境での PATH 問題

Docker や CI/CD 環境では、PATH がローカルと異なるため、/usr/local/bin/usr/bin の中にあるコマンドが見つからない場合があります。
特に node:alpine ベースのコンテナでは、軽量化のために多くの実行ツールが含まれていません。

対策:

  • 実際のコンテナ内で PATH を確認する echo $PATH which node which bash
  • 必要なツールをインストールする RUN apk add --no-cache bash python3
  • Node.js 側で絶対パスを指定する、または shell: true を使う

5. ファイル・スクリプト名のミス

単純なファイル名の打ち間違いや、拡張子の違い(例: .sh.bash)も原因のひとつです。
また、相対パスを指定した場合に、実行ディレクトリ (process.cwd()) が期待と違うこともよくあります。

spawn('./scripts/deploy.sh') // ❌ 実行ディレクトリが別なら見つからない

対策:

import path from 'path'
spawn(path.resolve(__dirname, 'scripts/deploy.sh'))

例: cross-platform で安全に使う spawn

異なる OS でも安定して動かすためには、次のように shell: true を使うのが簡単です。

const { spawn } = require('child_process')

const cmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'
const child = spawn(cmd, ['run', 'build'], {
  stdio: 'inherit',
  shell: true
})

child.on('error', (err) => {
  console.error('Spawn failed:', err)
})

これなら Windows・Mac・Linux いずれでも ENOENT が発生しにくくなります。

まとめ

「Error: spawn ENOENT」は Node.js がコマンドを実行できないときに発生する典型的な環境依存エラーです。
原因の多くは「PATH が通っていない」「拡張子の違い」「実行権限がない」といった単純な設定ミスです。
特に次のポイントをチェックすれば、ほとんどのケースで解決できます。

  • 絶対パスを指定してみる
  • Windows では .cmd.exe を意識する
  • shell: true を使ってシェル解決を有効化する
  • Docker や CI では PATH と権限を明示的に設定する

開発環境と本番環境で PATH が異なる場合、spawn のような低レベル API よりも execacross-spawn といったラッパーライブラリを使うのも実用的です。

参考

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