¿Cómo depuro "Error: spawn ENOENT" en node.js?

350

Cuando recibo el siguiente error:

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)

¿Qué procedimiento puedo seguir para solucionarlo?

Nota del autor : Muchos problemas con este error me animaron a publicar esta pregunta para futuras referencias.

Preguntas relacionadas:

laconbass
fuente
En mi caso, estaba pasando todo el comando como una cadena como lo haría con usted en execlugar de pasar el comando como primer argumento y las opciones como una matriz para el segundo argumento. Por ejemplo, estaba haciendo en spawn( "adb logcat -c" )lugar de spawn( "adb", [ "logcat", "-c" ] ).
Joshua Pinter

Respuestas:

235

NOTA: Este error casi siempre se debe a que el comando no existe, porque el directorio de trabajo no existe o por un error solo de Windows.

Encontré una manera fácil y particular de tener la idea de la causa raíz de:

Error: spawn ENOENT

El problema de este error es que hay muy poca información en el mensaje de error que le indique dónde se encuentra el sitio de la llamada, es decir, qué ejecutable / comando no se encuentra, especialmente cuando tiene una base de código grande donde hay muchas llamadas engendradas . Por otro lado, si conocemos el comando exacto que causa el error, entonces podemos seguir la respuesta de @laconbass para solucionar el problema.

Encontré una manera muy fácil de detectar qué comando causa el problema en lugar de agregar oyentes de eventos en todo el código, como se sugiere en la respuesta de @laconbass. La idea clave es ajustar la llamada de generación original con un contenedor que imprime los argumentos enviados a la llamada de generación.

Aquí está la función de contenedor, colóquela en la parte superior del index.jsscript de inicio de su servidor o lo que sea.

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

Luego, la próxima vez que ejecute su aplicación, antes del mensaje de excepción no detectada, verá algo así:

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

De esta manera, puede saber fácilmente qué comando se ejecuta realmente y luego puede descubrir por qué nodejs no puede encontrar el ejecutable para solucionar el problema.

Jiaji Zhou
fuente
3
He aquí otra idea: sólo cambia spawn()a exec()e inténtelo de nuevo. exec()le dirá qué comando intentó ejecutar.
Adam Monsen
1
Importante: asegúrese de colocar el código de arriba lo más cerca posible del inicio del archivo JS principal. Si carga otros módulos primero, pueden esconder la función 'spawn' y la anulación aquí nunca se llamará.
Dan Nissenbaum
1
No tengo suerte con el guión. No funciona en absoluto.
newguy
Entonces, ¿cómo usarías este método en un archivo gruñido? No estoy seguro de dónde poner esto.
Felix Eve
2
Esto funcionó perfectamente para mí. Acabo de poner esto en la parte superior de mi archivo gulpfile.js, y bingo bango bongo, ¡engendro el registro!
Yann Duran
121

Paso 1: asegúrese de que spawnse llame de la manera correcta

Primero, revise los documentos para child_process.spawn (comando, argumentos, opciones) :

Inicia un nuevo proceso con el dado command, con argumentos de línea de comando en args. Si se omite, el valor argspredeterminado es una matriz vacía.

El tercer argumento se usa para especificar opciones adicionales, que por defecto son:

{ cwd: undefined, env: process.env }

Se usa envpara especificar variables de entorno que serán visibles para el nuevo proceso, el valor predeterminado es process.env.

Asegúrese de no poner ningún argumento en la línea de comandos commandy toda la spawnllamada es válida . Proceda al siguiente paso.

Paso 2: identifique el emisor de eventos que emite el evento de error

Busque en su código fuente cada llamada a spawn, o child_process.spawn, es decir,

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

y adjunte un detector de eventos para el evento 'error', de modo que se note el Emisor de eventos exacto que lo está arrojando como 'No controlado'. Después de la depuración, ese controlador puede eliminarse.

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

Ejecute y debería obtener la ruta del archivo y el número de línea donde se registró su escucha de 'error'. Algo como:

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

Si las dos primeras líneas siguen siendo

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

repita este paso hasta que no lo estén. Debe identificar al oyente que emite el error antes de continuar con el siguiente paso.

Paso 3: asegúrese de que la variable de entorno $PATHesté configurada

Hay dos escenarios posibles:

  1. Confía en el spawncomportamiento predeterminado , por lo que el entorno de proceso secundario será el mismo que process.env.
  2. Usted está explícitamente pasar un envobjeto a spawnen el optionsargumento.

En ambos escenarios, debe inspeccionar la PATHclave en el objeto de entorno que utilizará el proceso secundario generado.

Ejemplo para el escenario 1

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

Ejemplo para el escenario 2

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

La ausencia de PATH(es decir, es undefined) hará spawnque se emita el ENOENTerror , ya que no será posible localizar ninguno a commandmenos que sea una ruta absoluta al archivo ejecutable.

Cuando PATHestá configurado correctamente, continúe con el siguiente paso. Debe ser un directorio o una lista de directorios. El último caso es el habitual.

Paso 4: asegúrese de que commandexista en un directorio de los definidos enPATH

Spawn puede emitir el ENOENTerror si el nombre de archivo command(es decir, 'algún comando') no existe en al menos uno de los directorios definidos en PATH.

Localice el lugar exacto de command. En la mayoría de las distribuciones de Linux, esto se puede hacer desde una terminal con el whichcomando. Le indicará la ruta absoluta al archivo ejecutable (como arriba), o le indicará si no se encuentra.

Ejemplo de uso de cuál y su salida cuando se encuentra un comando

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

Ejemplo de uso de cuál y su salida cuando no se encuentra un comando

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

Los programas mal instalados son la causa más común de un comando no encontrado . Consulte la documentación de cada comando si es necesario e instálelo.

Cuando el comando es un archivo de script simple, asegúrese de que sea accesible desde un directorio en el PATH. Si no es así, muévalo a uno o haga un enlace a él.

Una vez que determine que PATHestá configurado correctamente y commandes accesible desde él, debería poder generar el proceso de su hijo sin spawn ENOENTser arrojado.

laconbass
fuente
1
Esto ha sido muy útil para mi depuración de Spawn ENOENT. Lo he referenciado varias veces. ¡Gracias!
CodeManiak
36
También descubrí que ENOENT se generará si lo especifica cwden las opciones, pero el directorio dado no existe.
Daniel Imfeld
44
@DanielImfeld SALVADOR TOTAL. Deberías escribir una respuesta que diga esto.
GreenAsJade
44
Cuando se utiliza spawn('some-command', ['--help'], { env: env });como se ejemplifica en el paso 3 en esta respuesta y está pasando un entorno personalizado, asegúrese de especificar el PATH, por ejemplo: { env: { PATH: process.env.PATH } }. La opción env no heredará las variables de su env actual por defecto.
Anty
55
Pude resolver mi problema pasando shell: truea las opciones de generación.
Nickofthyme
35

Como lo señaló @DanielImfeld , ENOENT se generará si especifica "cwd" en las opciones, pero el directorio dado no existe.

Leeroy Brun
fuente
1
Entonces, ¿hay alguna manera de ejecutar el comando en un directorio específico?
Mitro
En Windows (7) parece que también debe incluir la letra de la unidad en la cwdruta: 'c: / ...' y no solo '/ ...'
Museful
29

Solución de Windows: Reemplazar spawncon nodo-cross-spawn . Por ejemplo, así al principio de tu app.js:

(function() {
    var childProcess = require("child_process");
    childProcess.spawn = require('cross-spawn');
})(); 
Nilzor
fuente
2
funcionó excepto que es un drop-in, no hay necesidad de child_process. Exactamente de la misma manera que spawn o spawnSync del nodo, por lo que es una caída en el reemplazo. var spawn = require('cross-spawn'); // Spawn NPM asynchronously var child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
Bogdan Trusca
27

La respuesta de @ laconbass me ayudó y probablemente sea la más correcta.

Vine aquí porque estaba usando spawn incorrectamente. Como un simple ejemplo:

Esto es incorrecto:

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

Esto es incorrecto:

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

esto es correcto:

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

Sin embargo, recomiendo hacerlo de esta manera:

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

Esto se debe a que el cp.on('exit', fn)evento siempre se disparará, siempre y cuando bash esté instalado, de lo contrario, el cp.on('error', fn)evento podría dispararse primero, si lo usamos de la primera manera, si lanzamos 'npm' directamente.

Alexander Mills
fuente
1
Pensando en refactorizar mi respuesta para proporcionar una "guía general" y dejar detalles para cada causa del problema (dependencias perdidas, llamadas incorrectas, entorno incorrecto, ...).
laconbass
2
a todos los que les guste esta respuesta, también pueden estar interesados ​​en esta alternativa nativa: gist.github.com/ORESoftware/7bf225f0045b4649de6848f1ea5def4c
Alexander Mills
1
Downvoted porque si lo que quiere es tener una cáscara entonces debe usar child_process.execo pasar shell: truea spawn.
givanse
@givanse no es necesariamente cierto: es posible que desee ejecutar zsh o bash o fsh según el shell que desee usar, y el comportamiento también es diferente
Alexander Mills
22

Para ENOENT en Windows, https://github.com/nodejs/node-v0.x-archive/issues/2318#issuecomment-249355505 corríjalo.

por ejemplo, reemplazar spawn ('npm', ['-v'], {stdio: 'heredar'}) con:

  • para todas las versiones de node.js:

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

    spawn('npm', ['-v'], {stdio: 'inherit', shell: true})
Li Zheng
fuente
1
¿Dónde hacer estas modificaciones?
Deilan
8
La parte clave es agregarshell: true
Ted Nyberg
19

Para cualquiera que pueda tropezar con esto, si todas las otras respuestas no ayudan y usted está en Windows, sepa que actualmente hay un gran problema con spawnWindows y la PATHEXTvariable de entorno que puede hacer que ciertas llamadas se generen para que no funcionen, dependiendo de cómo El comando de destino está instalado.

Alex Turpin
fuente
2
¿Y cuál es la solución?
Nilzor
66
Usar node-cross-spawn funcionó para mí. Vea la respuesta a continuación: stackoverflow.com/a/35561971/507339
Nilzor
1
Pasé años tratando de encontrar lo que estaba mal y esto terminó siendo el problema. Me di por vencido spawny simplemente usé en su execlugar.
Reducked
8

En mi caso, recibí este error debido a que no se instalaron los recursos del sistema dependientes necesarios.

Más específicamente, tengo una aplicación NodeJS que está utilizando ImageMagick. A pesar de tener instalado el paquete npm, el núcleo Linux ImageMagick no estaba instalado. Hice un apt-get para instalar ImageMagick y después de eso todo funcionó muy bien.

PromInc
fuente
¿Windows también necesita ImageMagick instalado? Estoy probando en Windows y
obtengo
6

en windows, simplemente agregar la shell: trueopción resolvió mi problema:

incorrecto:

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

correcto:

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

¿Estás cambiando la envopción?

Entonces mira esta respuesta.


Estaba tratando de generar un proceso de nodo y TIL que debería difundir las variables de entorno existentes cuando genera, de lo contrario perderá la PATHvariable de entorno y posiblemente otras importantes.

Esta fue la solución para mí:

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
fuente
4

Antes de que alguien pase mucho tiempo depurando este problema, la mayoría de las veces se puede resolver eliminando node_modulesy reinstalando los paquetes.

Instalar:

Si existe un archivo de bloqueo, puede usar

yarn install --frozen-lockfile

o

npm ci

respetuosamente si no entonces

yarn install

o

npm i
InsOp
fuente
¡Guau, una solución tan simple y funcionó para mí! Todos deberían probar esto primero para ver si resuelve el problema.
Nick K
2

Me encontré con el mismo problema, pero encontré una manera simple de solucionarlo. Parece que hay spawn()errores si el usuario ha agregado el programa a la RUTA (por ejemplo, los comandos normales del sistema funcionan).

Para solucionar esto, puede usar el módulo 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
fuente
2

¡Use en require('child_process').execlugar de generar para un mensaje de error más específico!

por ejemplo:

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
fuente
1

Asegúrese de que el módulo a ejecutar esté instalado o la ruta completa al comando si no es un módulo de nodo

Dalton
fuente
1

También estaba pasando por este molesto problema mientras ejecutaba mis casos de prueba, así que intenté muchas formas de superarlo. Pero la forma en que funciona para mí es ejecutar su corredor de prueba desde el directorio que contiene su archivo principal que incluye su función de generación de nodejs algo como esto:

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

Por ejemplo, este nombre de archivo es test.js , así que simplemente muévase a la carpeta que lo contiene . En mi caso, es una carpeta de prueba como esta:

cd root/test/

luego de ejecutar su corredor de prueba en mi caso, es moca, así será así:

mocha test.js

He perdido más de un día para resolverlo. ¡¡Disfrutar!!

Rajkumar Bansal
fuente
1

Me encontré con este problema en Windows, donde llamar execy spawncon exactamente el mismo comando (omitir argumentos) funcionó bien exec(así que sabía que mi comando estaba activado $PATH), pero spawndaría ENOENT. Resultó que solo necesitaba agregar .exeel comando que estaba 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']);
Mayormente sin brazos
fuente
0

Recibía este error cuando intentaba depurar un programa node.js desde el editor VS Code en un sistema Debian Linux. Noté que lo mismo funcionaba bien en Windows. Las soluciones dadas anteriormente aquí no fueron de mucha ayuda porque no había escrito ningún comando de "spawn". El código ofensivo fue presumiblemente escrito por Microsoft y oculto bajo el capó del programa VS Code.

Luego noté que node.js se llama nodo en Windows pero en Debian (y presumiblemente en sistemas basados ​​en Debian como Ubuntu) se llama nodejs. Así que creé un alias: desde un terminal raíz, ejecuté

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

Y esto resolvió el problema. Presumiblemente, el mismo procedimiento o uno similar funcionará en otros casos donde su node.js se llama nodejs pero está ejecutando un programa que espera que se llame nodo, o viceversa.

MTGradwell
fuente
0

Si está en Windows Node.js hace algunos negocios divertidos cuando maneja citas que pueden resultar en que emita un comando que sabe que funciona desde la consola, pero no cuando se ejecuta en Node. Por ejemplo, lo siguiente debería funcionar:

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

pero falla Hay una opción fantásticamente indocumentada windowsVerbatimArgumentspara manejar citas / similares que parece ser el truco, solo asegúrese de agregar lo siguiente a su objeto de opciones:

const opts = {
    windowsVerbatimArguments: true
};

y tu comando debería estar de vuelta en el negocio.

 spawn('ping', ['"8.8.8.8"'], { windowsVerbatimArguments: true });
Joel B
fuente
No cite los argumentos dentro de la matriz
laconbass
@laconbass Este es un ejemplo obviamente trivial para transmitir el concepto y, por lo tanto, las citas podrían eliminarse. Sin embargo, hay casos en los que absolutamente necesita citar los argumentos (por ejemplo, si necesita pasar un argumento que tiene una ruta con un espacio en él: "C: \ Archivos de programa \ ..." ). Lo publiqué aquí porque, aunque puede que no haya sido la causa de su caso de error específico, espero que ayude a alguien más a experimentar este error críptico debido al manejo de citas de Node en Windows como me estaba encontrando.
Joel B
node.js ya hace algo de magia negra y silenciosamente cita argumentos "correctamente". Su ejemplo debería funcionar sin la opción indocumentada que menciona, quitando el argumento dentro de la matriz.
laconbass
Solo para agregar mi propia experiencia, estaba ejecutando un proceso java desde el nodo. Este error me sucedió debido a las comillas alrededor del comando, en lugar del argumento. Prueba con espacios en la ruta de comando y aún funciona sin comillas
Troncoso
0

solución en mi 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');
Dan Alboteanu
fuente
1
Si bien esta puede ser una solución para ganar soluciones específicas, no veo cómo ayuda a depurar la causa real de ENOENT
laconbass
No tengo idea de por qué, pero la llamada de generación funcionaría en la respuesta del nodo sin el .cmd, pero falla en una prueba de broma mecanografiada. - Este error puede ser bastante difícil de entender, esta respuesta merece más votos a favor.
Mathieu CAROFF
0

En caso de que experimente este problema con una aplicación cuyo origen no puede modificar, considere invocarlo con la variable de entorno NODE_DEBUGestablecida child_process, por ejemplo NODE_DEBUG=child_process yarn test. Esto le proporcionará información sobre qué líneas de comando se han invocado en qué directorio y, por lo general, el último detalle es el motivo del error.

Karl Richter
fuente
0

Aunque puede ser una ruta de entorno u otro problema para algunas personas, acabo de instalar la extensión Latex Workshop para Visual Studio Code en Windows 10 y vi este error al intentar construir / previsualizar el PDF. Ejecutar VS Code como administrador me resolvió el problema.

Steve
fuente
1
De nuevo, la ruta relacionada del sistema de archivos do de alguna manera. La extensión probablemente no puede alcanzar una ruta sin permisos de administrador
laconbass
-1

Obtuve el mismo error para Windows 8. El problema se debe a que falta una variable de entorno de la ruta del sistema. Agregue el valor "C: \ Windows \ System32 \" a la variable PATH de su sistema.

Chaya Sandamali
fuente
-2

Agregar C:\Windows\System32\a la pathvariable de entorno.

Pasos

  1. Ir a mi computadora y propiedades

  2. Haga clic en Configuración avanzada

  3. Luego en variables de entorno

  4. Seleccione Pathy luego haga clic en editar

  5. Pegue lo siguiente si aún no está presente: C:\Windows\System32\

  6. Cerrar el símbolo del sistema

  7. Ejecute el comando que desea ejecutar

Captura de pantalla de variables de entorno de Windows 8

vmit dhawan
fuente
3
Este es un duplicado de la respuesta
Emile Bergeron