Come posso eseguire il debug di "Errore: spawn ENOENT" su node.js?


350

Quando viene visualizzato il seguente errore:

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)

Quale procedura posso seguire per risolverlo?

Nota dell'autore : molti problemi con questo errore mi hanno incoraggiato a pubblicare questa domanda per riferimenti futuri.

Domande correlate:


Nel mio caso, stavo passando l'intero comando come una stringa come faresti tu execinvece di passare il comando come primo argomento e le opzioni come matrice per il secondo argomento. ad es. stavo facendo spawn( "adb logcat -c" )invece di spawn( "adb", [ "logcat", "-c" ] ).
Joshua Pinter

Risposte:


235

NOTA: questo errore è quasi sempre causato perché il comando non esiste, perché la directory di lavoro non esiste o da un bug solo di Windows.

Ho trovato un modo particolarmente semplice per avere l'idea della causa principale di:

Error: spawn ENOENT

Il problema di questo errore è che ci sono davvero poche informazioni nel messaggio di errore per dirti dove si trova il sito di chiamata, ovvero quale eseguibile / comando non viene trovato, specialmente quando si dispone di una base di codice di grandi dimensioni dove ci sono molte chiamate di spawn . D'altra parte, se conosciamo il comando esatto che causa l'errore, possiamo seguire la risposta di @laconbass per risolvere il problema.

Ho trovato un modo molto semplice per individuare quale comando causa il problema piuttosto che aggiungere listener di eventi ovunque nel codice, come suggerito nella risposta di @laconbass. L'idea chiave è di avvolgere la chiamata di spawn originale con un wrapper che stampa gli argomenti inviati alla chiamata di spawn.

Ecco la funzione wrapper, mettila nella parte superiore index.jso qualunque sia lo script iniziale del tuo server.

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

Quindi la prossima volta che avvierai la tua applicazione, prima del messaggio dell'eccezione non rilevata vedrai qualcosa del genere:

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

In questo modo puoi facilmente sapere quale comando viene effettivamente eseguito e quindi puoi scoprire perché nodejs non riesce a trovare l'eseguibile per risolvere il problema.


3
Ecco un'altra idea: basta cambiare spawn()per exec()e riprovare. exec()ti dirà quale comando ha tentato di eseguire.
Adam Monsen,

1
Importante: assicurati di posizionare il codice sopra il più vicino possibile all'inizio del file JS principale. Se si caricano prima altri moduli, questi possono nascondere la funzione "spawn" e la sostituzione qui non verrà mai chiamata.
Dan Nissenbaum,

1
Non ho fortuna ad usare la sceneggiatura. Non funziona affatto.
Newguy,

Quindi come useresti questo metodo in un file grunt? Non sono sicuro di dove metterlo.
Felix Eve

2
Ha funzionato perfettamente per me. Ho appena inserito questo in cima al mio file gulpfile.js e bingo bango bongo, spawn logging!
Yann Duran,

121

Passaggio 1: assicurarsi che spawnsia chiamato nel modo giusto

Innanzitutto, esamina i documenti per child_process.spawn (comando, args, opzioni) :

Avvia un nuovo processo con il dato command, con gli argomenti della riga di comando in args. Se omesso, viene argsimpostato automaticamente un array vuoto.

Il terzo argomento viene utilizzato per specificare ulteriori opzioni, che per impostazione predefinita è:

{ cwd: undefined, env: process.env }

Utilizzare envper specificare le variabili di ambiente che saranno visibili al nuovo processo, l'impostazione predefinita è process.env.

Assicurati di non inserire argomenti da riga di comando commande che l'intera spawnchiamata sia valida . Procedere con il passaggio successivo.

Passaggio 2: identificare l'Emettitore di eventi che emette l'evento di errore

Cerca nel tuo codice sorgente ogni chiamata a spawn, o child_process.spawn, ad es

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

e allega un listener di eventi per l'evento "errore", così noterai l'esatto Emettitore di eventi che lo sta lanciando come "Non gestito". Dopo il debug, quel gestore può essere rimosso.

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

Eseguire e si dovrebbe ottenere il percorso del file e il numero di riga in cui è stato registrato il listener "errore". Qualcosa di simile a:

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

Se le prime due righe sono ferme

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

ripetere questo passaggio fino a quando non lo sono. È necessario identificare il listener che emette l'errore prima di procedere con il passaggio successivo.

Passaggio 3: assicurarsi che la variabile di ambiente $PATHsia impostata

Esistono due possibili scenari:

  1. Ti affidi al spawncomportamento predefinito , quindi l'ambiente di processo figlio sarà lo stesso di process.env.
  2. Sei esplicitamente passaggio di un envoggetto spawnsulla optionstesi.

In entrambi gli scenari, è necessario ispezionare la PATHchiave sull'oggetto ambiente che verrà utilizzata dal processo figlio generato.

Esempio per lo scenario 1

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

Esempio per lo scenario 2

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

L'assenza di PATH(cioè, è undefined) causerà spawnl'emissione ENOENTdell'errore , in quanto non sarà possibile individuarne uno a commandmeno che non sia un percorso assoluto per il file eseguibile.

Quando PATHè impostato correttamente, procedere al passaggio successivo. Dovrebbe essere una directory o un elenco di directory. L'ultimo caso è il solito.

Passaggio 4: assicurarsi che commandesista in una directory di quelli definiti inPATH

Spawn può emettere l' ENOENTerrore se il nome file command(ovvero, 'some-command') non esiste in almeno una delle directory definite PATH.

Individua il luogo esatto di command. Sulla maggior parte delle distribuzioni di Linux, questo può essere fatto da un terminale con il whichcomando. Ti dirà il percorso assoluto del file eseguibile (come sopra) o dirà se non è stato trovato.

Esempio di utilizzo di quale e il suo output quando viene trovato un comando

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

Esempio di utilizzo di quale e il suo output quando non viene trovato un comando

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

i programmi installati per errore sono la causa più comune di un comando non trovato . Fare riferimento alla documentazione di ciascun comando, se necessario, e installarlo.

Quando il comando è un semplice file di script, assicurarsi che sia accessibile da una directory sul PATH. In caso contrario, spostalo su uno o creane un collegamento.

Una volta stabilito che PATHè impostato correttamente e commandaccessibile da esso, dovresti essere in grado di spawnare il processo figlio senza spawn ENOENTessere lanciato.


1
Questo è stato molto utile per il mio debug di Spawn ENOENT. L'ho fatto riferimento più volte. Grazie!
CodeManiak,

36
Ho anche scoperto che ENOENT verrà lanciato se si specifica cwdnelle opzioni, ma la directory fornita non esiste.
Daniel Imfeld,

4
@DanielImfeld TOTAL SAVIOR. Dovresti scrivere una risposta che dice questo.
GreenAsJade,

4
Quando si utilizza spawn('some-command', ['--help'], { env: env });come esemplificato dalla Fase 3 in questa risposta e passa un ambiente personalizzato, accertarsi di specificare la PATH, per esempio: { env: { PATH: process.env.PATH } }. L'opzione env non erediterà le variabili dal tuo env corrente per impostazione predefinita.
anty

5
Sono stato in grado di risolvere il mio problema passando shell: truealle opzioni di spawn.
Nickofthyme,

35

Come ha indicato @DanielImfeld , ENOENT verrà lanciato se si specifica "cwd" nelle opzioni, ma la directory fornita non esiste.


1
quindi c'è un modo per eseguire il comando in una directory specifica?
Mitro,

In Windows (7) sembra che sia necessario includere anche la lettera di unità nel cwdpercorso: 'c: / ...' e non solo '/ ...'
Museful

29

Soluzione Windows: sostituire spawncon nodo-spawn incrociato . Ad esempio come questo all'inizio di app.js:

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

2
ha funzionato tranne che è un drop-in, non c'è bisogno di child_process. Esattamente allo stesso modo di spawn o spawnSync del nodo, quindi è un calo nella sostituzione. var spawn = require('cross-spawn'); // Spawn NPM asynchronously var child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
Bogdan Trusca,

27

La risposta di @ laconbass mi ha aiutato ed è probabilmente la più corretta.

Sono venuto qui perché stavo usando spawn in modo errato. Come semplice esempio:

questo non è corretto:

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

questo non è corretto:

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

questo è corretto:

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

tuttavia, ti consiglio di farlo in questo modo:

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

questo perché l' cp.on('exit', fn)evento si attiva sempre, purché sia ​​installato bash, altrimenti l' cp.on('error', fn)evento potrebbe attivarsi per primo, se lo utilizziamo nel primo modo, se avvieremo direttamente 'npm'.


1
Pensando al refactoring della mia risposta per fornire una "guida generale" e lasciando dettagli a ciascuna causa del problema (mancate dipendenze, chiamate errate, ambiente sbagliato, ...).
laconbass,

2
tutti coloro a cui piace questa risposta, potrebbero anche essere interessati a questa alternativa nativa: gist.github.com/ORESoftware/7bf225f0045b4649de6848f1ea5def4c
Alexander Mills,

1
Sottovalutato perché se ciò che si desidera avere è una shell, è necessario utilizzare child_process.execo passare shell: truea spawn.
givanse,

@givanse non necessariamente vero - potresti voler eseguire zsh o bash o fsh a seconda della shell che vuoi usare, e anche il comportamento è diverso
Alexander Mills,

22

Per ENOENT su Windows, https://github.com/nodejs/node-v0.x-archive/issues/2318#issuecomment-249355505 risolvilo .

ad es. sostituire spawn ('npm', ['-v'], {stdio: 'inherit'}) con:

  • per tutte le versioni di node.js:

    spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['-v'], {stdio: 'inherit'})
  • per node.js 5.xe versioni successive:

    spawn('npm', ['-v'], {stdio: 'inherit', shell: true})

1
Dove effettuare queste modifiche?
Deilan,

8
La parte chiave sta aggiungendoshell: true
Ted Nyberg il

19

Per chiunque possa inciampare su questo, se tutte le altre risposte non aiutano e sei su Windows, sappi che attualmente esiste un grosso problema con spawnWindows e la PATHEXTvariabile di ambiente che può causare il mancato funzionamento di determinate chiamate a seconda di come il comando target è installato.


2
E qual è la soluzione?
Nilzor,

6
L'uso di node-cross-spawn ha funzionato per me. Vedi risposta qui sotto: stackoverflow.com/a/35561971/507339
Nilzor

1
Passarono anni a cercare di scoprire cosa non andava e questo finì per essere il problema. Ho rinunciato spawne usato solo execinvece.
reduckted

8

Nel mio caso, questo errore veniva generato a causa delle risorse di sistema dipendenti necessarie che non venivano installate.

Più specificamente, ho un'app NodeJS che utilizza ImageMagick. Nonostante abbia installato il pacchetto npm, il core ImageMagick di Linux non è stato installato. Ho fatto apt-get per installare ImageMagick e dopo tutto ha funzionato alla grande!


Windows ha bisogno anche di ImageMagick installato? Sto testando Windows e
ricevo un

6

in Windows, semplicemente aggiungendo l' shell: trueopzione risolto il mio problema:

non corretta:

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

corretta:

const { spawn } = require('child_process');
const child = spawn('dir', [], {shell: true});

5

Stai cambiando l' envopzione?

Quindi guarda questa risposta.


Stavo cercando di generare un processo nodo e TIL che dovresti diffondere le variabili di ambiente esistenti quando spawn altrimenti perderai la PATHvariabile di ambiente e forse altre importanti.

Questa è stata la soluzione per me:

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

Prima che qualcuno dedichi molto tempo al debug di questo problema, il più delle volte può essere risolto eliminando node_modulese reinstallando i pacchetti.

Installare:

Se esiste un file di blocco, è possibile utilizzarlo

yarn install --frozen-lockfile

o

npm ci

respectivly. se no allora

yarn install

o

npm i

Wow una soluzione così semplice e ha funzionato per me! Tutti dovrebbero provare prima questo per vedere se risolve il problema.
Nick K,

2

Ho riscontrato lo stesso problema, ma ho trovato un modo semplice per risolverlo. Sembra essere un spawn()errore se il programma è stato aggiunto al PERCORSO dall'utente (ad es. I normali comandi di sistema funzionano).

Per risolvere questo problema, è possibile utilizzare il cui modulo ( 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

Usa require('child_process').execinvece di spawn per un messaggio di errore più specifico!

per esempio:

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

Assicurarsi che il modulo da eseguire sia installato o percorso completo da comandare se non è un modulo nodo


1

Stavo anche attraversando questo fastidioso problema mentre eseguivo i miei casi di test, quindi ho provato molti modi per superarlo. Ma il modo in cui funziona per me è quello di eseguire il test runner dalla directory che contiene il file principale che include la funzione di spawn nodejs in questo modo:

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

Ad esempio, questo nome file è test.js , quindi passa alla cartella che lo contiene . Nel mio caso, è la cartella di test come questa:

cd root/test/

quindi da run runner nel mio caso è la sua moka, quindi sarà così:

mocha test.js

Ho sprecato il mio più di un giorno per capirlo. Godere!!


1

Mi sono imbattuto in questo problema su Windows, dove chiamare exece spawncon lo stesso comando esatto (omettendo argomenti) ha funzionato bene exec(quindi sapevo che il mio comando era attivo $PATH), ma spawndarebbe ENOENT. Si è scoperto che avevo solo bisogno di aggiungere .exeal comando che stavo usando:

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

Stavo ottenendo questo errore quando provavo a eseguire il debug di un programma node.js dall'editor VS Code su un sistema Debian Linux. Ho notato che la stessa cosa ha funzionato bene su Windows. Le soluzioni precedentemente fornite qui non sono state di grande aiuto perché non avevo scritto alcun comando "spawn". Il codice offensivo è stato presumibilmente scritto da Microsoft e nascosto sotto il cappuccio del programma VS Code.

Successivamente ho notato che node.js si chiama nodo su Windows ma su Debian (e presumibilmente su sistemi basati su Debian come Ubuntu) si chiama nodejs. Quindi ho creato un alias - da un terminale di root, ho corso

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

e questo ha risolto il problema. La stessa procedura o una procedura simile funzioneranno presumibilmente in altri casi in cui il tuo node.js è chiamato nodejs ma stai eseguendo un programma che si aspetta che venga chiamato node o viceversa.


0

Se sei su Windows Node.js fa affari divertenti quando gestisci le virgolette che potrebbero farti emettere un comando che sai che funziona dalla console, ma non quando viene eseguito in Node. Ad esempio, dovrebbe funzionare quanto segue :

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

ma fallisce. C'è un'opzione straordinariamente non documentata windowsVerbatimArgumentsper la gestione di virgolette / simili che sembra fare il trucco, assicurati di aggiungere quanto segue al tuo oggetto opts:

const opts = {
    windowsVerbatimArguments: true
};

e il tuo comando dovrebbe essere di nuovo in affari.

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

Non citare gli argomenti all'interno dell'array
laconbass,

@laconbass Questo è ovviamente un esempio banale per trasmettere il concetto e quindi le virgolette potrebbero essere rimosse. Tuttavia, ci sono casi in cui è assolutamente necessario citare gli argomenti (ad esempio se è necessario passare un argomento che contiene un percorso con uno spazio al suo interno: "C: \ Programmi \ ..." ). L'ho pubblicato qui perché, anche se potrebbe non essere stata la causa del tuo caso di errore specifico, si spera che possa aiutare qualcun altro a riscontrare questo errore criptico a causa della gestione delle citazioni di Node su Windows come stavo incontrando.
Gioele B,

node.js rende già alcuni Black Magic e cita silenziosamente gli argomenti "correttamente". L'esempio dovrebbe funzionare senza l'opzione non documentata menzionata, annullando la citazione dell'argomento all'interno dell'array.
Laconbass,

Solo per aggiungere la mia esperienza, stavo eseguendo un processo Java dal nodo. Questo errore mi è successo a causa delle virgolette attorno al comando, piuttosto che dell'argomento. Prova con spazi nel percorso del comando e funziona ancora senza virgolette
Troncoso

0

soluzione nel mio caso

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
Mentre questa può essere una soluzione per vincere correzioni specifiche, non vedo come aiuta a eseguire il debug della vera causa dell'ENOENT
laconbass

Non ho idea del perché, ma la chiamata spawn funzionerebbe nel nodo di sostituzione senza il .cmd, ma fallirebbe in un test jest dattiloscritto. - Questo errore può essere abbastanza difficile da capire, questa risposta merita più voti.
Mathieu CAROFF,

0

Nel caso in cui si riscontri questo problema con un'applicazione di cui non è possibile modificare l'origine, prendere in considerazione la possibilità di richiamarlo con la variabile di ambiente NODE_DEBUGimpostata su child_process, ad es NODE_DEBUG=child_process yarn test. Questo ti fornirà informazioni su quali righe di comando sono state invocate in quale directory e di solito l'ultimo motivo è il motivo dell'errore.


0

Sebbene possa trattarsi di un percorso ambientale o di un altro problema per alcune persone, avevo appena installato l'estensione Latex Workshop per Visual Studio Code su Windows 10 e ho visto questo errore durante il tentativo di compilare / visualizzare in anteprima il PDF. L'esecuzione di VS Code come amministratore ha risolto il problema per me.


1
Ancora una volta, correlato fare il percorso del filesystem in qualche modo. L'estensione probabilmente non può raggiungere un percorso senza le autorizzazioni di amministratore
laconbass

-1

Ho avuto lo stesso errore per Windows 8. Il problema è dovuto alla mancanza di una variabile di ambiente del percorso di sistema. Aggiungi il valore "C: \ Windows \ System32 \" alla variabile PATH del tuo sistema.


-2

Aggiungi C:\Windows\System32\alla pathvariabile d'ambiente.

passi

  1. Vai al mio computer e proprietà

  2. Fai clic su Impostazioni avanzate

  3. Quindi su variabili d'ambiente

  4. Selezionare Pathe quindi fare clic su Modifica

  5. Incolla quanto segue se non è già presente: C:\Windows\System32\

  6. Chiudi il prompt dei comandi

  7. Esegui il comando che desideri eseguire

Schermata delle variabili di ambiente di Windows 8


3
Questo è un duplicato della risposta
Emile Bergeron,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.