Як я налагоджую помилку "Помилка: spawn 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)

Яку процедуру я можу дотримуватися, щоб її виправити?

Примітка автора : Дуже багато проблем із цією помилкою спонукало мене опублікувати це питання для подальшого використання.

Пов’язані запитання:


У моєму випадку я передавав цілу команду як String, як ви, execа не команду як перший аргумент, а параметри як Array для другого аргументу. наприклад, я робив spawn( "adb logcat -c" )замість цього spawn( "adb", [ "logcat", "-c" ] ).
Джошуа Пінтер

Відповіді:


235

ПРИМІТКА. Ця помилка майже завжди викликається тим, що команда не існує, оскільки робочий каталог не існує, або з помилки лише для Windows.

Я знайшов особливий простий спосіб зрозуміти першопричину:

Error: spawn ENOENT

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

Я знайшов дуже простий спосіб визначити, яка команда викликає проблему, а не додавати слухачів подій скрізь у свій код, як було запропоновано у відповіді @laconbass. Ключова ідея - обернути оригінальний нерестовий виклик обгорткою, яка друкує аргументи, що надсилаються на нерестовий виклик.

Ось функція обгортки, помістіть її вгорі 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

То як би ви використовували цей метод у грунт-файлі? Я не впевнений, куди це поставити.
Фелікс Єва

2
Це прекрасно працювало для мене. Я просто помістив це у верхній частині мого файлу gulpfile.js, і бінго-банго-бонго, нерестовий журнал!
Ян Дюран

121

Крок 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' ]);

і підключіть туди слухача події для події "помилка", тож ви помітите точного випромінювача події, який кидає його як "Нерозроблений". Після налагодження цей обробник можна буде видалити.

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

Нерест може видавати ENOENTпомилку, якщо ім'я файлу 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кидаючи її.


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.
anty

5
Мені вдалося вирішити свою проблему, перейшовши shell: trueдо варіантів нересту.
Нікофтіме

35

Як вказував @DanielImfeld , ENOENT буде викинутий, якщо ви вкажете "cwd" у параметрах, але даний каталог не існує.


1
так чи є спосіб виконати в конкретному каталозі команду?
Мітро

У Windows (7), здається, вам також потрібно включити літеру диска в cwdшлях: 'c: / ...', а не просто '/ ...'
Помітно

29

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

(function() {
    var childProcess = require("child_process");
    childProcess.spawn = require('cross-spawn');
})(); 

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)подія завжди буде спрацьовувати, доки встановлений bash, інакше cp.on('error', fn)подія може спершу розпочатись, якщо ми будемо використовувати її першим способом, якщо ми запустимо 'npm' безпосередньо.


1
Думаю про рефакторинг моєї відповіді, щоб надати "загальний посібник" та залишити деталі щодо кожної причини проблеми (пропустіть залежності, неправильні дзвінки, неправильне оточення, ...).
лакобас

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/isissue/2318#issuecomment-249355505 виправте це.

наприклад, замініть spawn ('npm', ['-v'], {stdio: 'nasledit'}) на:

  • для всіх версій 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
Використання вузла-крос-нересту працювало для мене. Див відповідь нижче: stackoverflow.com/a/35561971/507339
Nilzor

1
Провели віки, намагаючись знайти те, що було не так, і це врешті-решт стало проблемою. Я здавався spawnі просто використовував execзамість цього.
скорочено

8

У моєму випадку я отримував цю помилку через відсутність необхідних системних ресурсів.

Більш конкретно, у мене є додаток NodeJS, який використовує ImageMagick. Незважаючи на встановлення пакету npm, основний Linux ImageMagick не був встановлений. Я зробив сприятливий спосіб встановити ImageMagick, і після цього все працювало чудово!


Чи потрібен також встановлений ImageMagick Windows? Я тестую 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

шанобливо. якщо не тоді

yarn install

або

npm i

Ух, таке просте рішення, і воно спрацювало на мене! Кожен повинен спробувати це спочатку, щоб побачити, чи вирішує це питання.
Нік К

2

Я зіткнувся з тією ж проблемою, але знайшов простий спосіб її виправити. Виявляються spawn()помилки, якщо користувач додав програму до PATH (наприклад, працюють звичайні системні команди).

Щоб виправити це, ви можете використовувати будь модуль ( 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замість нересту для більш конкретного повідомлення про помилку!

наприклад:

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

Переконайтеся, що встановлений модуль встановлений або повний шлях до команди, якщо це не модуль вузла


1

Я також переживав цю набридливу проблему під час запуску тестових випадків, тому спробував багато способів подолати це. Але спосіб для мене полягає в тому, щоб запустити тестовий бігун з каталогу, який містить ваш основний файл, який включає вашу функцію нересту 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']);

0

Я отримував цю помилку при спробі налагодження програми node.js з редактора VS Code в системі Debian Linux. Я помітив, що те саме працювало нормально у Windows. Раніше наведені тут рішення не дуже допомагали, оскільки я не писав жодних команд "spawn". Код правопорушення, імовірно, був написаний корпорацією Майкрософт та захований під кришкою програми VS Code.

Далі я помітив, що node.js називається node в Windows, але в Debian (і, імовірно, в системах на базі Debian, таких як Ubuntu) він називається nodejs. Тож я створив псевдонім - з кореневого терміналу я побіг

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

і це вирішило проблему. Ця ж чи подібна процедура, ймовірно, буде працювати в інших випадках, коли ваш node.js називається nodejs, але ви запускаєте програму, яка очікує, що вона буде називатися вузлом, або навпаки.


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 \ ..." ). Я розмістив це тут, тому що, хоча це, можливо, і не було причиною вашої конкретної помилки, воно, сподіваємось, допоможе комусь іншому відчути цю криптовану помилку через обробку цитат у Windows, як я стикався.
Джоель Б

node.js вже робить деякі Black Magic і мовчки цитує аргументи "належним чином". Ваш приклад повинен працювати без недокументованого варіанту, який ви згадуєте, знімаючи аргументи всередині масиву.
лаконбас

Просто, щоб додати власний досвід, я запустив процес 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

Я не маю поняття, чому, але нерегулярний виклик буде працювати в репліку вузла без .cmd, але не в тесті шрифтів машинопису. - Ця помилка може бути досить складно зрозуміти, ця відповідь заслуговує на більшу кількість відгуків.
Матьє КАРОФФ

0

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


0

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


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
Це дублікат відповіді чаясана
Еміль Бергерон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.