Как мне отладить «Ошибка: вызов ENOENT» на node.js?

350

Когда я получаю следующую ошибку:

events.js:72
        throw er; // Unhandled 'error' event
              ^
Error: spawn ENOENT
    at errnoException (child_process.js:1000:11)
    at Process.ChildProcess._handle.onexit (child_process.js:791:34)

Какую процедуру я могу выполнить, чтобы исправить это?

Примечание автора : множество проблем с этой ошибкой побудило меня опубликовать этот вопрос для будущих ссылок.

Смежные вопросы:

laconbass
источник
В моем случае я передавал всю команду как строку, как вы, execвместо того, чтобы передавать команду в качестве первого аргумента и параметры в качестве массива для второго аргумента. например, я делал spawn( "adb logcat -c" )вместо spawn( "adb", [ "logcat", "-c" ] ).
Джошуа Пинтер

Ответы:

235

ПРИМЕЧАНИЕ. Эта ошибка почти всегда возникает из-за того, что команда не существует, потому что рабочий каталог не существует или из-за ошибки Windows.

Я нашел очень простой способ понять причину:

Error: spawn ENOENT

Проблема этой ошибки в том, что в сообщении об ошибке действительно мало информации, чтобы сказать вам, где находится сайт вызова, т.е. какой исполняемый файл / команда не найден, особенно если у вас большая кодовая база, где много вызовов вызова , С другой стороны, если мы знаем точную команду, которая вызывает ошибку, тогда мы можем следовать ответу @laconbass, чтобы решить проблему.

Я нашел очень простой способ определить, какая команда вызывает проблему, вместо добавления прослушивателей событий в вашем коде, как предложено в ответе @laconbass. Основная идея заключается в том, чтобы обернуть исходный вызов spawn оболочкой, которая печатает аргументы, отправленные вызову spawn.

Вот функция-обертка, поместите ее в начало index.jsили любой другой сценарий запуска вашего сервера.

(function() {
    var childProcess = require("child_process");
    var oldSpawn = childProcess.spawn;
    function mySpawn() {
        console.log('spawn called');
        console.log(arguments);
        var result = oldSpawn.apply(this, arguments);
        return result;
    }
    childProcess.spawn = mySpawn;
})();

Затем в следующий раз, когда вы запустите ваше приложение, перед сообщением с неперехваченным исключением вы увидите что-то вроде этого:

spawn called
{ '0': 'hg',
  '1': [],
  '2':
   { cwd: '/* omitted */',
     env: { IP: '0.0.0.0' },
     args: [] } }

Таким образом, вы можете легко узнать, какая команда на самом деле выполняется, а затем вы можете узнать, почему nodejs не может найти исполняемый файл для решения проблемы.

Цзяцзи Чжоу
источник
3
Вот еще одна идея: просто перейдите spawn()на exec()и попробуйте снова. exec()скажет вам, какую команду он пытался запустить.
Адам Монсен
1
Важное замечание: Убедитесь, что код выше расположен как можно ближе к началу основного файла JS. Если вы сначала загрузите другие модули, они могут спрятать функцию spawn, и переопределение здесь никогда не будет вызываться.
Дан Ниссенбаум
1
Мне не повезло с использованием сценария. Это не работает вообще.
Newguy
Так как бы вы использовали этот метод в файле grunt? Я не уверен, где это поставить.
Феликс Ева
2
Это отлично сработало для меня. Я просто поместил это наверху моего файла gulpfile.js, и bingo bango bongo, spawn logging!
Янн Дюран
122

Шаг 1: Убедитесь, что spawnэто правильный путь

Сначала просмотрите документы для child_process.spawn (команда, аргументы, параметры) :

Запускает новый процесс с заданными commandаргументами командной строки в args. Если опущено, по argsумолчанию используется пустой массив.

Третий аргумент используется для указания дополнительных параметров, которые по умолчанию:

{ cwd: undefined, env: process.env }

Используется envдля указания переменных среды, которые будут видны новому процессу, по умолчанию process.env.

Убедитесь, что вы не вводите никаких аргументов командной строки, commandи весь spawnвызов действителен . Перейдите к следующему шагу.

Шаг 2. Определите источник событий, который генерирует событие ошибки

Поиск по исходному коду для каждого звонка spawnили child_process.spawn, т. Е.

spawn('some-command', [ '--help' ]);

и прикрепите туда прослушиватель событий для события error, чтобы вы точно заметили Emitter, который выбрасывает его как «Unhandled». После отладки этот обработчик может быть удален.

spawn('some-command', [ '--help' ])
  .on('error', function( err ){ throw err })
;

Выполните, и вы должны получить путь к файлу и номер строки, где был зарегистрирован ваш прослушиватель ошибок. Что-то вроде:

/file/that/registers/the/error/listener.js:29
      throw err;
            ^
Error: spawn ENOENT
    at errnoException (child_process.js:1000:11)
    at Process.ChildProcess._handle.onexit (child_process.js:791:34)

Если первые две строки еще

events.js:72
        throw er; // Unhandled 'error' event

сделайте этот шаг снова, пока их нет. Вы должны определить слушателя, который выдает ошибку, прежде чем перейти к следующему шагу.

Шаг 3. Убедитесь, что переменная среды $PATH установлена

Есть два возможных сценария:

  1. Вы полагаетесь на spawnповедение по умолчанию , поэтому среда дочерних процессов будет такой же, как и process.env.
  2. Вы просто передаете envобъект spawnв optionsаргумент.

В обоих сценариях вы должны проверить PATHключ объекта среды, который будет использоваться порожденным дочерним процессом.

Пример для сценария 1

// inspect the PATH key on process.env
console.log( process.env.PATH );
spawn('some-command', ['--help']);

Пример для сценария 2

var env = getEnvKeyValuePairsSomeHow();
// inspect the PATH key on the env object
console.log( env.PATH );
spawn('some-command', ['--help'], { env: env });

Отсутствие PATH(т.е. это undefined) заставит spawnизлучать ENOENTошибку , так как это не будет возможно найти какой - либо , commandесли это не абсолютный путь к исполняемому файлу.

Когда PATHправильно установлено, переходите к следующему шагу.Это должен быть каталог или список каталогов. Последний случай обычный.

Шаг 4: Убедитесь, что commandсуществует в каталоге тех, которые определены вPATH

Spawn может выдать ENOENTошибку, если имя файла command(то есть 'some-command') не существует хотя бы в одном из каталогов, определенных в PATH.

Найдите точное место command. В большинстве дистрибутивов Linux это можно сделать из терминала с помощью whichкоманды. Он скажет вам абсолютный путь к исполняемому файлу (как выше), или скажет, если он не найден.

Пример использования которого и его вывод, когда команда найдена

> which some-command
some-command is /usr/bin/some-command

Пример использования которого и его вывод, когда команда не найдена

> which some-command
bash: type: some-command: not found

неправильно установленные программы являются наиболее частой причиной не найденной команды. При необходимости обратитесь к документации по каждой команде и установите ее.

Когда команда представляет собой простой файл сценария, убедитесь, что он доступен из каталога на PATH. Если это не так, либо переместите его к одному, либо сделайте ссылку на него.

Как только вы определите, что PATHон правильно настроен и commandдоступен из него, вы сможете создавать дочерний процесс, не spawn ENOENTвызывая его.

laconbass
источник
1
Это было очень полезно для моей отладки Spawn ENOENT. Я ссылался на это несколько раз. Спасибо!
CodeManiak
36
Я также обнаружил, что ENOENT будет выброшен, если вы укажете cwdв опциях, но данный каталог не существует.
Даниэль Имфельд
4
@DanielImfeld ПОЛНЫЙ СПАСАТЕЛЬ. Вы должны написать ответ, который говорит это.
GreenAsJade
4
При использовании в spawn('some-command', ['--help'], { env: env });качестве примера на шаге 3 в этом ответе и проходят пользовательскую среду, обязательно указать PATH, например: { env: { PATH: process.env.PATH } }. Опция env не будет наследовать переменные от вашего текущего env по умолчанию.
Анты
5
Я смог решить мою проблему, перейдя shell: trueк опциям появления.
Nickofthyme
35

Как указал @DanielImfeld , ENOENT будет выброшен, если вы укажете «cwd» в опциях, но данный каталог не существует.

Лирой Брун
источник
1
так есть ли способ выполнить в определенном каталоге команду?
Митро
В Windows (7) кажется, что вам также необходимо включить в cwdпуть букву диска : «c: / ...», а не просто «/ ...»
Museful
29

Решение для Windows: замените spawnна узел-перекрестный спавн . Например, вот так в начале вашего app.js:

(function() {
    var childProcess = require("child_process");
    childProcess.spawn = require('cross-spawn');
})(); 
Nilzor
источник
2
работал, за исключением того, что это вставка, нет необходимости в child_process. Точно так же, как spawn или spawnSync узла, так что это капля замены. var spawn = require('cross-spawn'); // Spawn NPM asynchronously var child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
Богдан Труска
27

Ответ @ laconbass мне помог и, наверное, самый правильный.

Я пришел сюда, потому что я использовал спавн неправильно. В качестве простого примера:

это неверно:

const s = cp.spawn('npm install -D suman', [], {
    cwd: root
});

это неверно:

const s = cp.spawn('npm', ['install -D suman'], {
    cwd: root
});

это верно:

const s = cp.spawn('npm', ['install','-D','suman'], {
    cwd: root
});

Тем не менее, я рекомендую сделать это следующим образом:

const s = cp.spawn('bash');
s.stdin.end(`cd "${root}" && npm install -D suman`);
s.once('exit', code => {
   // exit
});

это потому, что тогда cp.on('exit', fn)событие будет всегда cp.on('error', fn)запускаться, пока установлена ​​bash, в противном случае событие может запускаться первым, если мы используем его первым способом, если мы запускаем 'npm' напрямую.

Александр Миллс
источник
1
Подумайте над рефакторингом моего ответа, чтобы предоставить «общее руководство», и оставьте детали для каждой причины проблемы (пропуски зависимостей, неправильные вызовы, неправильная среда, ...).
Laconbass
2
все, кому нравится этот ответ, могут также заинтересоваться этой нативной альтернативой: gist.github.com/ORESoftware/7bf225f0045b4649de6848f1ea5def4c
Александр Миллс
1
Понижено, потому что если то, что вы хотите иметь, это оболочка, то вам следует использовать child_process.execили перейти shell: trueк spawn.
Givanse
@givanse не обязательно верно - вы можете запускать zsh или bash или fsh в зависимости от того, какую оболочку вы хотите использовать, и поведение также отличается
Александр Миллс
22

Для ENOENT в Windows https://github.com/nodejs/node-v0.x-archive/issues/2318#issuecomment-249355505 это исправить.

например, заменить spawn ('npm', ['-v'], {stdio: 'наследовать'}) на:

  • для всей версии node.js:

    spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['-v'], {stdio: 'inherit'})
  • для node.js 5.x и выше:

    spawn('npm', ['-v'], {stdio: 'inherit', shell: true})
Ли Чжэн
источник
1
Где делать эти модификации?
Дейлан
8
Ключевая часть добавляетshell: true
Тед Найберг
19

Для тех, кто может наткнуться на это, если все остальные ответы не помогают, и вы находитесь в Windows, знайте, что в настоящее время существует большая проблема с spawnWindows и PATHEXTпеременная окружения, которая может привести к тому, что определенные вызовы не будут работать, в зависимости от того, как целевая команда установлена.

Алекс Турпин
источник
2
И какое решение?
Нильзор
6
Использование node-cross-spawn работало на меня. Смотрите ответ ниже: stackoverflow.com/a/35561971/507339
Нильзор,
1
Потратил целую вечность, пытаясь найти то, что было не так, и это оказалось проблемой. Я сдался spawnи просто использовал execвместо этого.
сокращенный
8

В моем случае я получал эту ошибку из-за того, что не были установлены необходимые зависимые системные ресурсы.

В частности, у меня есть приложение NodeJS, которое использует ImageMagick. Несмотря на установленный пакет npm, ядро ​​Linux ImageMagick не было установлено. Я сделал apt-get для установки ImageMagick, и после этого все работало отлично!

PromInc
источник
Нужно ли устанавливать Windows в ImageMagick? Я тестирую на Windows и получаю сообщение об ошибке
Somename
6

в Windows, просто добавив shell: trueопцию, решил мою проблему:

неправильно:

const { spawn } = require('child_process');
const child = spawn('dir');

верный:

const { spawn } = require('child_process');
const child = spawn('dir', [], {shell: true});
Ашкан Насирзаде
источник
5

Вы меняете envопцию?

Тогда посмотрите на этот ответ.


Я пытался порождать процесс узла и TIL, чтобы вы распространяли существующие переменные окружения, когда вы порождаете, иначе вы потеряете PATHпеременную окружения и, возможно, другие важные.

Это было исправление для меня:

const nodeProcess = spawn('node', ['--help'], {
  env: {
    // by default, spawn uses `process.env` for the value of `env`
    // you can _add_ to this behavior, by spreading `process.env`
    ...process.env,
    OTHER_ENV_VARIABLE: 'test',
  }
});
Рико Калер
источник
4

Прежде чем кто-либо потратит много времени на отладку этой проблемы, большую часть времени она может быть решена путем удаления node_modulesи переустановки пакетов.

Установить:

Если существует файл блокировки, вы можете использовать

yarn install --frozen-lockfile

или

npm ci

respectivly. если нет то

yarn install

или

npm i
InsOp
источник
Ничего себе такое простое решение, и оно сработало для меня! Каждый должен сначала попробовать это, чтобы увидеть, решит ли это проблему.
Ник К
2

Я столкнулся с той же проблемой, но нашел простой способ ее исправить. Это кажется spawn()ошибкой, если программа была добавлена ​​в PATH пользователем (например, работают нормальные системные команды).

Чтобы это исправить, вы можете использовать which module ( npm install --save which):

// Require which and child_process
const which = require('which');
const spawn = require('child_process').spawn;
// Find npm in PATH
const npm = which.sync('npm');
// Execute
const noErrorSpawn = spawn(npm, ['install']);
Жвачка Джо
источник
2

Используйте require('child_process').execвместо spawn для более конкретного сообщения об ошибке!

например:

var exec = require('child_process').exec;
var commandStr = 'java -jar something.jar';

exec(commandStr, function(error, stdout, stderr) {
  if(error || stderr) console.log(error || stderr);
  else console.log(stdout);
});
де Раад
источник
1

Убедитесь, что модуль для выполнения установлен или полный путь к команде, если это не модуль узла

Dalton
источник
1

Я также проходил через эту досадную проблему, выполняя свои тестовые случаи, поэтому я попробовал много способов решить эту проблему. Но способ для меня - запустить ваш тестовый прогон из каталога, в котором находится ваш главный файл, который включает в себя функцию spawn для nodejs, примерно так:

nodeProcess = spawn('node',params, {cwd: '../../node/', detached: true });

Например, это имя файла test.js , поэтому просто перейдите в папку, в которой он находится . В моем случае это тестовая папка:

cd root/test/

тогда от запуска вашего тестового бегуна в моем случае его мокко, так что это будет так:

mocha test.js

Я потратил больше одного дня, чтобы понять это. Наслаждаться!!

Раджкумар Бансал
источник
1

Я столкнулся с этой проблемой в Windows, где вызов execи spawnс точно такой же командой (без аргументов) работал нормально exec(поэтому я знал, что моя команда была включена $PATH), но spawnдал ENOENT. Оказалось, что мне просто нужно добавить .exeкоманду, которую я использовал:

import { exec, spawn } from 'child_process';

// This works fine
exec('p4 changes -s submitted');

// This gives the ENOENT error
spawn('p4');

// But this resolves it
spawn('p4.exe');
// Even works with the arguments now
spawn('p4.exe', ['changes', '-s', 'submitted']);
MostlyArmless
источник
0

Я получал эту ошибку при попытке отладки программы node.js из редактора кода VS в системе Debian Linux. Я заметил, что та же самая вещь работала хорошо на Windows. Решения, ранее приведенные здесь, не сильно помогли, потому что я не написал никаких команд "spawn". Оскорбительный код предположительно был написан Microsoft и спрятан под капотом программы VS Code.

Затем я заметил, что node.js называется node в Windows, но в Debian (и, вероятно, в системах на основе Debian, таких как Ubuntu) он называется nodejs. Итак, я создал псевдоним - из корневого терминала я запустил

ln -s / usr / bin / nodejs / usr / local / bin / node

и это решило проблему. Эта же или аналогичная процедура, вероятно, будет работать в других случаях, когда ваш node.js называется nodejs, но вы запускаете программу, которая ожидает, что он будет назван node, или наоборот.

MTGradwell
источник
0

Если вы работаете в Windows, Node.js делает несколько забавных операций с кавычками, что может привести к тому, что вы введете команду, которая, как вы знаете, работает из консоли, но не работает при работе в Node. Например, должно работать следующее :

spawn('ping', ['"8.8.8.8"'], {});

но не удается. Есть фантастически недокументированная опция windowsVerbatimArgumentsдля обработки кавычек / подобных, которая, кажется, делает свое дело, просто добавьте следующее к вашему объекту opts:

const opts = {
    windowsVerbatimArguments: true
};

и ваша команда должна вернуться в дело.

 spawn('ping', ['"8.8.8.8"'], { windowsVerbatimArguments: true });
Джоэл Б
источник
Не
цитируйте
@laconbass Это, очевидно, тривиальный пример для передачи концепции, поэтому цитаты могут быть удалены. Однако есть случаи, когда вам абсолютно необходимо заключать аргументы в кавычки (например, если вам нужно передать аргумент, в котором есть путь с пробелом: «C: \ Program Files \ ...» ). Я разместил это здесь, потому что, хотя это, возможно, и не было причиной вашего конкретного случая ошибки, мы надеемся, что это поможет кому-то еще, столкнувшемуся с этой загадочной ошибкой из-за того, что Node обрабатывает кавычки в Windows, как я столкнулся.
Джоэл Б,
node.js уже создает Черную Магию и молча цитирует аргументы «правильно». Ваш пример должен работать без недокументированного параметра, который вы упомянули, путем кавычки аргумента внутри массива.
лаконбас
Просто чтобы добавить свой собственный опыт, я запускал Java-процесс с узла. Эта ошибка произошла со мной из-за кавычек вокруг команды, а не аргумента. Тест с пробелами в командной строке, и он все еще работает без кавычек
Troncoso
0

решение в моем случае

var spawn = require('child_process').spawn;

const isWindows = /^win/.test(process.platform); 

spawn(isWindows ? 'twitter-proxy.cmd' : 'twitter-proxy');
spawn(isWindows ? 'http-server.cmd' : 'http-server');
Дэн Алботяну
источник
1
Хотя это может быть решением для конкретных исправлений, я не понимаю, как это поможет отладить истинную причину ENOENT
laconbass
Я понятия не имею, почему, но вызов spawn будет работать в repl узла без .cmd, но потерпит неудачу в тесте jest. - Эта ошибка может быть довольно трудно понять, этот ответ заслуживает большего количества голосов.
Матье КАРОФФ
0

В случае , если вы столкнулись с этой проблемой с приложением, источником которого вы не можете изменить рассмотреть применение его с помощью переменной окружения NODE_DEBUGнабора к child_process, например NODE_DEBUG=child_process yarn test. Это предоставит вам информацию о том, какие командные строки были вызваны, в каком каталоге и, как правило, последняя деталь является причиной сбоя.

Карл Рихтер
источник
0

Хотя для некоторых людей это может быть путь к среде или другая проблема, я только что установил расширение Latex Workshop для кода Visual Studio в Windows 10 и увидел эту ошибку при попытке создать / просмотреть PDF. Запуск VS Code от имени администратора решил проблему для меня.

Стив
источник
1
Опять же, связанные пути файловой системы. Расширение , вероятно , не может достигнуть пути без разрешения администратора
laconbass
-1

Я получил ту же ошибку для Windows 8. Проблема в том, что отсутствует переменная окружения вашего системного пути. Добавьте значение «C: \ Windows \ System32 \» в системную переменную PATH.

Чая Сандамали
источник
-2

Добавить C:\Windows\System32\кpath переменную окружения.

меры

  1. Перейти на мой компьютер и свойства

  2. Нажмите на Дополнительные настройки

  3. Тогда по переменным среды

  4. Выберите, Pathа затем нажмите на редактировать

  5. Вставьте следующее, если его еще нет: C:\Windows\System32\

  6. Закройте командную строку

  7. Запустите команду, которую вы хотели запустить

Скриншот переменных среды Windows 8

Вмит Даван
источник
3
Это дубликат ответа Чаясана
Эмиль Бержерон