Jak debugować „Błąd: spawn ENOENT” na node.js?

350

Kiedy pojawia się następujący błąd:

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)

Jaką procedurę mogę wykonać, aby to naprawić?

Notka autora : Wiele problemów z tym błędem zachęciło mnie do opublikowania tego pytania do przyszłych odniesień.

Powiązane pytania:

laconbass
źródło
W moim przypadku przekazałem całe polecenie jako ciąg znaków, tak jak ty, execzamiast przekazać polecenie jako pierwszy argument, a opcje jako tablicę dla drugiego argumentu. np. robiłem spawn( "adb logcat -c" )zamiast spawn( "adb", [ "logcat", "-c" ] ).
Joshua Pinter

Odpowiedzi:

235

UWAGA: Ten błąd jest prawie zawsze powodowany, ponieważ polecenie nie istnieje, ponieważ katalog roboczy nie istnieje lub z powodu błędu tylko w systemie Windows.

Znalazłem szczególnie łatwy sposób, aby uzyskać pojęcie o podstawowej przyczynie:

Error: spawn ENOENT

Problem polega na tym, że w komunikacie o błędzie jest naprawdę niewiele informacji, które wskazują, gdzie znajduje się strona wywoływania, tzn. Który plik wykonywalny / polecenie nie został znaleziony, zwłaszcza gdy masz dużą bazę kodu, w której jest wiele wywołań spawn . Z drugiej strony, jeśli znamy dokładne polecenie, które powoduje błąd, możemy postępować zgodnie z odpowiedzią @laconbass, aby rozwiązać problem.

Znalazłem bardzo łatwy sposób na wykrycie, które polecenie powoduje problem, zamiast dodawania detektorów zdarzeń w całym kodzie, jak sugeruje odpowiedź @laconbass. Kluczową ideą jest zawinięcie pierwotnego wywołania spawn za pomocą opakowania, które wypisuje argumenty wysłane do wywołania spawn.

Oto funkcja otoki, umieść ją na górze index.jsskryptu startowego serwera lub cokolwiek innego.

(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;
})();

Następnie przy następnym uruchomieniu aplikacji, przed komunikatem o nieprzechwyconym wyjątku, zobaczysz coś takiego:

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

W ten sposób możesz łatwo wiedzieć, które polecenie jest faktycznie wykonywane, a następnie dowiedzieć się, dlaczego nodejs nie może znaleźć pliku wykonywalnego, aby rozwiązać problem.

Jiaji Zhou
źródło
3
Oto kolejny pomysł: wystarczy zmienić spawn()się exec()i spróbuj ponownie. exec()powie ci, jakie polecenie próbował uruchomić.
Adam Monsen
1
Ważne: upewnij się, że umieściłeś powyższy kod jak najbliżej początku głównego pliku JS. Jeśli najpierw załadujesz inne moduły, mogą one ukryć funkcję „spawn” i zastąpienie tutaj nigdy nie zostanie wywołane.
Dan Nissenbaum
1
Nie mam szczęścia, używając skryptu. To w ogóle nie działa.
newguy,
Jak więc użyłbyś tej metody w pliku pomruk? Nie jestem pewien, gdzie to położyć.
Felix Eve
2
To działało idealnie dla mnie. Po prostu umieściłem to na górze mojego pliku gulpfile.js i bingo bango bongo, rejestrowanie spawn!
Yann Duran
121

Krok 1: Upewnij spawnsię, że nazywa się właściwą drogę

Najpierw przejrzyj dokumentację child_process.spawn (polecenie, argumenty, opcje) :

Uruchamia nowy proces z podanym commandargumentem wiersza poleceń w args. Jeśli zostanie pominięty, argsdomyślnie jest pusta tablica.

Trzeci argument służy do określenia dodatkowych opcji, które domyślnie:

{ cwd: undefined, env: process.env }

Służy envdo określania zmiennych środowiskowych, które będą widoczne dla nowego procesu, domyślnie jest to process.env.

Upewnij się, że nie wstawiasz żadnych argumentów wiersza poleceń, commanda całe spawnwywołanie jest prawidłowe . Przejdź do następnego kroku.

Krok 2: Zidentyfikuj emiter zdarzeń, który emituje zdarzenie błędu

Wyszukaj w kodzie źródłowym każde połączenie do spawnlub child_process.spawn, tj

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

i dołącz tam detektor zdarzeń dla zdarzenia „błąd”, abyś zauważył dokładny emiter zdarzeń, który wyrzuca go jako „nieobsługiwany”. Po debugowaniu ten program obsługi można usunąć.

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

Wykonaj, a powinieneś uzyskać ścieżkę do pliku i numer wiersza, w którym zarejestrowany został Twój detektor błędu. Coś jak:

/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)

Jeśli pierwsze dwie linie są nadal

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

wykonaj ten krok ponownie, dopóki nie będą. Przed przejściem do następnego kroku musisz zidentyfikować nasłuchującego, który emituje błąd.

Krok 3: Upewnij się, że zmienna środowiskowa $PATHjest ustawiona

Istnieją dwa możliwe scenariusze:

  1. Polegasz na domyślnym spawnzachowaniu, więc środowisko procesów potomnych będzie takie samo jak process.env.
  2. Jesteś explicity przechodzącą envobiektu do spawnna optionsargument.

W obu scenariuszach należy sprawdzić PATHklucz obiektu środowiskowego, którego będzie używał odrodzony proces potomny.

Przykład dla scenariusza 1

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

Przykład dla scenariusza 2

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

Brak PATH(tj. Jest undefined) spowoduje spawnwyświetlenie ENOENTbłędu , ponieważ nie będzie można go zlokalizować, commandchyba że będzie to bezwzględna ścieżka do pliku wykonywalnego.

Po PATHprawidłowym ustawieniu przejdź do następnego kroku. Powinien to być katalog lub lista katalogów. Ostatni przypadek jest zwykły.

Krok 4: Upewnij się, że commandistnieje katalog w katalogu zdefiniowanym wPATH

Odrodzenie może wyemitować ENOENTbłąd, jeśli nazwa pliku command(tj. „Jakieś polecenie”) nie istnieje w co najmniej jednym z katalogów zdefiniowanych w PATH.

Znajdź dokładne miejsce command. W większości dystrybucji Linuksa można to zrobić z terminala za pomocą whichpolecenia. Powie ci bezwzględną ścieżkę do pliku wykonywalnego (jak wyżej) lub powie, czy go nie znaleziono.

Przykład użycia tego i jego wyniku po znalezieniu polecenia

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

Przykład użycia tego i jego wyniku, gdy nie znaleziono polecenia

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

źle zainstalowane programy są najczęstszą przyczyną nieznalezienia polecenia. W razie potrzeby zapoznaj się z dokumentacją każdego polecenia i zainstaluj ją.

Gdy polecenie jest prostym plikiem skryptu, upewnij się, że jest dostępny z katalogu w PATH. Jeśli nie, przenieś go do jednego lub stwórz link do niego.

Po ustaleniu, że PATHjest poprawnie ustawiony i commanddostępny z niego, powinieneś być w stanie odrodzić proces potomny bez spawn ENOENTrzucania.

laconbass
źródło
1
Było to bardzo pomocne przy debugowaniu Spawn ENOENT. Odwoływałem się do niego wiele razy. Dzięki!
CodeManiak,
36
Odkryłem również, że ENOENT zostanie wyrzucony, jeśli określisz cwdw opcjach, ale podany katalog nie istnieje.
Daniel Imfeld,
4
@DanielImfeld TOTAL SAVIOR. Powinieneś napisać odpowiedź, która to mówi.
GreenAsJade
4
Kiedy używasz spawn('some-command', ['--help'], { env: env });czego przykładem Etapu 3 w tej odpowiedzi i przechodzą środowisko niestandardową, należy określić PATH, na przykład: { env: { PATH: process.env.PATH } }. Opcja env domyślnie nie dziedziczy zmiennych z bieżącej env.
anty
5
Udało mi się rozwiązać problem, przechodząc shell: truedo opcji odradzania.
Nickofthyme
35

Jak wskazał @DanielImfeld , ENOENT zostanie wyrzucony, jeśli w opcjach podasz „cwd”, ale podany katalog nie istnieje.

Leeroy Brun
źródło
1
więc czy istnieje sposób na wykonanie polecenia w określonym katalogu?
Mitro
W systemie Windows (7) wydaje się, że w cwdścieżce należy również umieścić literę dysku : „c: / ...”, a nie tylko „/ ...”
Museful 15.01.19
29

Rozwiązanie Windows: Wymienić spawnz węzła-cross-tarło . Na przykład w ten sposób na początku pliku app.js:

(function() {
    var childProcess = require("child_process");
    childProcess.spawn = require('cross-spawn');
})(); 
Nilzor
źródło
2
działało, z wyjątkiem tego, że jest to drop-in, nie ma potrzeby child_process. Dokładnie tak samo jak spawn węzła lub spawnSync, więc jest to kropla zastępcza. var spawn = require('cross-spawn'); // Spawn NPM asynchronously var child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
Bogdan Trusca
27

Odpowiedź @ laconbass pomogła mi i jest prawdopodobnie najbardziej poprawna.

Przybyłem tutaj, ponieważ nieprawidłowo używałem spawn. Jako prosty przykład:

to jest niepoprawne:

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

to jest niepoprawne:

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

to jest poprawne:

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

jednak polecam zrobić to w ten sposób:

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

dzieje się tak, ponieważ wtedy cp.on('exit', fn)zdarzenie będzie zawsze uruchamiane, dopóki zainstalowana jest bash, w przeciwnym cp.on('error', fn)razie zdarzenie może zostać uruchomione jako pierwsze, jeśli użyjemy go w pierwszy sposób, jeśli uruchomimy bezpośrednio „npm”.

Alexander Mills
źródło
1
Zastanawiając się nad zmianą mojej odpowiedzi w celu uzyskania „ogólnego przewodnika” i pozostawieniem szczegółów dla każdej przyczyny problemu (brak zależności, nieprawidłowe połączenia, złe środowisko, ...).
laconbass 20.04.16
2
każdy, kto lubi tę odpowiedź, może również zainteresować się tą natywną alternatywą: gist.github.com/ORESoftware/7bf225f0045b4649de6848f1ea5def4c
Alexander Mills
1
Downvoted bo jeśli to, co chcesz mieć to skorupa należy użyć child_process.execlub przekazać shell: truedo spawn.
ofiarować
@givanse niekoniecznie jest prawdą - możesz chcieć uruchomić zsh, bash lub fsh w zależności od tego, jakiej powłoki chcesz użyć, a zachowanie jest również inne
Alexander Mills
22

W przypadku ENOENT w systemie Windows https://github.com/nodejs/node-v0.x-archive/issues/2318#issuecomment-249355505 to napraw.

np. zamień spawn ('npm', ['-v'], {stdio: 'inherit'}) na:

  • dla wszystkich wersji node.js:

    spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['-v'], {stdio: 'inherit'})
  • dla node.js 5.x i nowszych:

    spawn('npm', ['-v'], {stdio: 'inherit', shell: true})
Li Zheng
źródło
1
Gdzie dokonać tych modyfikacji?
Deilan,
8
Kluczowym elementem jest dodanieshell: true
Ted Nyberg,
19

Dla każdego, kto może się na to natknąć, jeśli wszystkie inne odpowiedzi nie pomogą i jesteś w systemie Windows, wiedz, że istnieje obecnie duży problem z spawnsystemem Windows i PATHEXTzmienną środowiskową, który może powodować, że niektóre wywołania nie będą działać w zależności od tego, jak polecenie docelowe jest zainstalowane.

Alex Turpin
źródło
2
A jakie jest rozwiązanie?
Nilzor
6
Korzystanie z odradzania węzłów działało dla mnie. Zobacz odpowiedź poniżej: stackoverflow.com/a/35561971/507339
Nilzor
1
Spędzone wieki próbują znaleźć coś, co jest nie tak, i to ostatecznie stanowi problem. Poddałem się spawni po prostu użyłem exec.
zredukowano
8

W moim przypadku ten błąd został zgłoszony z powodu niezainstalowania niezbędnych zależnych zasobów systemowych.

Mówiąc dokładniej, mam aplikację NodeJS, która wykorzystuje ImageMagick. Pomimo zainstalowanego pakietu npm, podstawowy Linux ImageMagick nie został zainstalowany. Zrobiłem apt-get, aby zainstalować ImageMagick, a potem wszystko działało świetnie!

PromInc
źródło
Czy system Windows wymaga również zainstalowanego ImageMagick?
Testuję w systemie
6

w systemie Windows dodanie shell: trueopcji rozwiązało mój problem:

błędny:

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

poprawny:

const { spawn } = require('child_process');
const child = spawn('dir', [], {shell: true});
ashkan nasirzadeh
źródło
5

Zmieniasz envopcję?

Następnie spójrz na tę odpowiedź.


Próbowałem spawnować proces węzła i TIL, że powinieneś rozprzestrzeniać istniejące zmienne środowiskowe, kiedy spawnujesz, inaczej stracisz PATHzmienną środowiskową i prawdopodobnie inne ważne.

To była poprawka dla mnie:

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',
  }
});
Rico Kahler
źródło
4

Zanim ktokolwiek poświęci dużo czasu na debugowanie tego problemu, przez większość czasu można go rozwiązać, usuwając node_modulesi ponownie instalując pakiety.

Żeby zainstalować:

Jeśli istnieje plik blokujący, możesz użyć

yarn install --frozen-lockfile

lub

npm ci

z szacunkiem. Jeśli nie wtedy

yarn install

lub

npm i
InsOp
źródło
Wow, takie proste rozwiązanie i zadziałało dla mnie! Każdy powinien spróbować najpierw, aby sprawdzić, czy to rozwiązuje problem.
Nick K
2

Natrafiłem na ten sam problem, ale znalazłem prosty sposób, aby go naprawić. Wygląda na spawn()błędy, jeśli program został dodany do ŚCIEŻKI przez użytkownika (np. Działają normalne polecenia systemowe).

Aby to naprawić, możesz użyć modułu which ( 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']);
Gum Joe
źródło
2

Użyj require('child_process').execzamiast spawn, aby uzyskać bardziej szczegółowy komunikat o błędzie!

na przykład:

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);
});
de Raad
źródło
1

Upewnij się, że moduł do wykonania jest zainstalowany lub pełna ścieżka do polecenia, jeśli nie jest to moduł węzła

Dalton
źródło
1

Miałem również do czynienia z tym irytującym problemem podczas uruchamiania moich testów, więc próbowałem go rozwiązać na wiele sposobów. Ale dla mnie sposób polega na uruchomieniu testera z katalogu, który zawiera twój główny plik, który zawiera twoją funkcję spawnowania nodejsa mniej więcej tak:

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

Na przykład ta nazwa pliku to test.js , więc po prostu przejdź do folderu, który ją zawiera . W moim przypadku jest to folder testowy taki jak ten:

cd root/test/

następnie uruchom swojego testera w moim przypadku jego mokkę, więc będzie to tak:

mocha test.js

Zmarnowałem więcej niż jeden dzień, żeby to rozgryźć. Cieszyć się!!

Rajkumar Bansal
źródło
1

Natknąłem się na ten problem w systemie Windows, gdzie zadzwonienie execi spawnprzy użyciu tego samego polecenia (pomijając argumenty) działało dobrze exec(więc wiedziałem, że moje polecenie jest włączone $PATH), ale spawndałoby ENOENT. Okazało się, że muszę tylko dołączyć .exedo polecenia, którego używałem:

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']);
Głównie bez ramienia
źródło
0

Ten błąd pojawiał się podczas próby debugowania programu node.js z poziomu edytora VS Code w systemie Linux Debian. Zauważyłem, że to samo działa OK w systemie Windows. Rozwiązania podane wcześniej nie były zbyt pomocne, ponieważ nie napisałem żadnych poleceń „spawn”. Obraźliwy kod został prawdopodobnie napisany przez Microsoft i ukryty pod maską programu VS Code.

Następnie zauważyłem, że node.js jest nazywany węzłem w systemie Windows, ale w Debianie (i prawdopodobnie w systemach opartych na Debianie, takich jak Ubuntu) nazywa się nodejs. Więc stworzyłem alias - z terminalu root uruchomiłem

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

i to rozwiązało problem. Ta sama lub podobna procedura będzie prawdopodobnie działać w innych przypadkach, gdy twój node.js jest nazywany nodejs, ale używasz programu, który oczekuje, że będzie się nazywał node, lub odwrotnie.

MTGradwell
źródło
0

Jeśli korzystasz z Windows Node.js, masz zabawny interes z obsługą cytatów, co może skutkować wydaniem polecenia, które działa z konsoli, ale nie działa po uruchomieniu w węźle. Na przykład następujące powinny działać:

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

ale zawodzi. Istnieje fantastycznie nieudokumentowana opcja windowsVerbatimArgumentsobsługi cytatów / podobnych, która wydaje się załatwić sprawę, po prostu dodaj następujące obiekty do obiektu opts:

const opts = {
    windowsVerbatimArguments: true
};

a twoje polecenie powinno wrócić do pracy.

 spawn('ping', ['"8.8.8.8"'], { windowsVerbatimArguments: true });
Joel B.
źródło
Nie
cytuj
@laconbass Jest to oczywiście trywialny przykład do przekazania koncepcji, dzięki czemu cytaty mogą zostać usunięte. Są jednak przypadki, w których absolutnie musisz zacytować argumenty (na przykład, jeśli musisz przekazać argument zawierający ścieżkę ze spacją: „C: \ Program Files \ ...” ). Zamieściłem go tutaj, ponieważ chociaż nie był on przyczyną konkretnego przypadku błędu, mam nadzieję, że pomoże to komuś innemu doświadczającemu tego tajemniczego błędu z powodu obsługi przez Node cudzysłowów w systemie Windows, tak jak ja.
Joel B,
node.js już tworzy Czarną Magię i po cichu cytuje argumenty „poprawnie”. Twój przykład powinien działać bez wspominanej nieudokumentowanej opcji, odznaczając argument w tablicy.
laconbass
Aby dodać własne doświadczenie, uruchomiłem proces Java z węzła. Ten błąd przydarzył mi się z powodu cudzysłowów wokół polecenia, a nie argumentu. Przetestuj ze spacjami w ścieżce polecenia i nadal działa bez cudzysłowów
Troncoso
0

rozwiązanie w moim przypadku

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');
Dan Alboteanu
źródło
1
Chociaż może to być rozwiązanie dla poprawek specyficznych dla wygranych, nie rozumiem, w jaki sposób pomaga debugować prawdziwą przyczynę ENOENT
laconbass
Nie mam pojęcia dlaczego, ale wywołanie spawn działałoby w replie węzła bez .cmd, ale nie powiodło się w teście maszynopisu. - Ten błąd może być dość trudny do zrozumienia, odpowiedzi te zasługują na więcej głosów pozytywnych.
Mathieu CAROFF
0

Jeśli masz ten problem z aplikacją, której źródła nie możesz zmodyfikować, rozważ wywołanie go ze zmienną środowiskową NODE_DEBUGustawioną child_processnp NODE_DEBUG=child_process yarn test. Dzięki temu dowiesz się, które wiersze poleceń zostały wywołane w którym katalogu, i zwykle ostatni szczegół jest przyczyną niepowodzenia.

Karl Richter
źródło
0

Chociaż może to być ścieżka środowiska lub inny problem dla niektórych osób, właśnie zainstalowałem rozszerzenie Latex Workshop dla programu Visual Studio Code w systemie Windows 10 i zobaczyłem ten błąd podczas próby kompilacji / podglądu pliku PDF. Uruchomienie VS Code jako Administrator rozwiązało problem dla mnie.

Steve
źródło
1
Ponownie, pokrewne do ścieżki systemu plików w jakiś sposób. Rozszerzenie prawdopodobnie nie może dotrzeć do ścieżki bez uprawnień administratora
laconbass
-1

Mam ten sam błąd w systemie Windows 8. Problem jest spowodowany brakiem zmiennej środowiskowej ścieżki systemowej. Dodaj wartość „C: \ Windows \ System32 \” do zmiennej systemowej PATH.

Chaya Sandamali
źródło
-2

Dodaj C:\Windows\System32\do pathzmiennej środowiskowej.

Kroki

  1. Przejdź do mojego komputera i właściwości

  2. Kliknij Ustawienia zaawansowane

  3. Następnie zmienne środowiskowe

  4. Wybierz, Patha następnie kliknij edytuj

  5. Wklej następujące, jeśli jeszcze nie są obecne: C:\Windows\System32\

  6. Zamknij wiersz polecenia

  7. Uruchom polecenie, które chcesz uruchomić

Zrzut ekranu zmiennych środowiskowych systemu Windows 8

vmit dhawan
źródło
3
To jest duplikat odpowiedzi Chayasana
Emile Bergeron,