Como depurar "Erro: gerar ENOENT" no node.js?

350

Quando recebo o seguinte erro:

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)

Que procedimento posso seguir para corrigi-lo?

Nota do autor : Muitos problemas com esse erro me incentivaram a postar esta pergunta para futuras referências.

Perguntas relacionadas:

laconbass
fonte
No meu caso, eu estava passando o comando inteiro como uma String como você faria, em execvez de passar o comando como o primeiro argumento e as opções como uma Matriz para o segundo argumento. por exemplo, eu estava fazendo em spawn( "adb logcat -c" )vez de spawn( "adb", [ "logcat", "-c" ] ).
Joshua Pinter

Respostas:

235

NOTA: Esse erro quase sempre é causado porque o comando não existe, porque o diretório de trabalho não existe ou devido a um bug somente do Windows.

Eu encontrei uma maneira particularmente fácil de entender a causa raiz de:

Error: spawn ENOENT

O problema desse erro é que há realmente pouca informação na mensagem de erro para informar onde está o site de chamada, ou seja, qual executável / comando não foi encontrado, especialmente quando você tem uma grande base de códigos em que há muitas chamadas de spawn . Por outro lado, se soubermos o comando exato que causa o erro, podemos seguir a resposta do @laconbass para corrigir o problema.

Encontrei uma maneira muito fácil de identificar qual comando causou o problema, em vez de adicionar ouvintes de eventos em todo o código, como sugerido na resposta do @laconbass. A idéia principal é agrupar a chamada de desova original com um invólucro que imprima os argumentos enviados para a chamada de desova.

Aqui está a função wrapper, coloque-a na parte superior do index.jsscript inicial do servidor ou seja lá o que for.

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

Da próxima vez que você executar seu aplicativo, antes da mensagem da exceção não capturada, você verá algo assim:

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

Dessa maneira, você pode saber facilmente qual comando realmente é executado e, em seguida, descobrir por que o nodejs não consegue encontrar o executável para corrigir o problema.

Jiaji Zhou
fonte
3
Aqui está outra idéia: basta mudar spawn()para exec()e tentar novamente. exec()lhe dirá qual comando ele tentou executar.
Adam Monsen
11
Importante: Certifique-se de colocar o código acima o mais próximo possível do início do arquivo JS principal. Se você carregar outros módulos primeiro, eles poderão ocultar a função 'spawn' e a substituição aqui nunca será chamada.
Dan Nissenbaum
11
Não tenho sorte usando o script. Não funciona de todo.
Newguy 3/11
Então, como você usaria esse método em um arquivo grunhido? Não sei onde colocar isso.
Felix Eve
2
Isso funcionou perfeitamente para mim. Acabei de colocar isso no topo do meu arquivo gulpfile.js, e o bingo bango bongo, gera registros!
Yann Duran
121

Etapa 1: verifique se spawné chamado o caminho certo

Primeiro, revise os documentos para child_process.spawn (command, args, options) :

Inicia um novo processo com o dado command, com argumentos de linha de comando em args. Se omitido, o argspadrão é uma matriz vazia.

O terceiro argumento é usado para especificar opções adicionais, cujo padrão é:

{ cwd: undefined, env: process.env }

Use envpara especificar variáveis ​​de ambiente que serão visíveis para o novo processo, o padrão é process.env.

Verifique se você não está colocando argumentos de linha de comando commande se toda a spawnchamada é válida . Prossiga para a próxima etapa.

Etapa 2: identificar o emissor do evento que emite o evento de erro

Pesquise no seu código-fonte cada chamada para spawn, ou child_process.spawn, ie

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

e anexe lá um ouvinte de evento para o evento 'error', para que você seja notado exatamente o emissor do evento que está lançando-o como 'Unhandled'. Após a depuração, esse manipulador pode ser removido.

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

Execute e você deve obter o caminho do arquivo e o número da linha em que o ouvinte 'erro' foi registrado. 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)

Se as duas primeiras linhas ainda estiverem

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

faça este passo novamente até que não estejam. Você deve identificar o ouvinte que emite o erro antes de prosseguir na próxima etapa.

Etapa 3: verifique se a variável de ambiente $PATHestá definida

Existem dois cenários possíveis:

  1. Você confia no spawncomportamento padrão , portanto, o ambiente do processo filho será o mesmo que process.env.
  2. Você está explicitamente passando um envobjeto para spawno optionsargumento.

Nos dois cenários, você deve inspecionar a PATHchave no objeto de ambiente que o processo filho gerado usará.

Exemplo para o cenário 1

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

Exemplo para o cenário 2

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

A ausência de PATH(ou seja, é undefined) causará spawno ENOENTerro , pois não será possível localizar nenhum, a commandmenos que seja um caminho absoluto para o arquivo executável.

Quando PATHestiver definido corretamente, prossiga para a próxima etapa. Deve ser um diretório ou uma lista de diretórios. O último caso é o habitual.

Etapa 4: verifique se commandexiste em um diretório daqueles definidos emPATH

O spawn pode emitir o ENOENTerro se o nome do arquivo command(por exemplo, 'algum comando') não existir em pelo menos um dos diretórios definidos em PATH.

Localize o local exato de command. Na maioria das distribuições linux, isso pode ser feito a partir de um terminal com o whichcomando Ele informará o caminho absoluto para o arquivo executável (como acima), ou informará se ele não foi encontrado.

Exemplo de uso de qual e sua saída quando um comando é encontrado

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

Exemplo de uso de qual e sua saída quando um comando não for encontrado

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

programas instalados com falta são a causa mais comum de um comando não encontrado . Consulte a documentação de cada comando, se necessário, e instale-o.

Quando o comando é um arquivo de script simples, verifique se ele é acessível a partir de um diretório no PATH. Caso contrário, mova-o para um ou faça um link para ele.

Depois de determinar que PATHestá definido corretamente e commandé acessível a partir dele, você poderá gerar o processo filho sem spawn ENOENTser jogado.

laconbass
fonte
11
Isso tem sido muito útil para minha depuração do Spawn ENOENT. Eu o referenciei várias vezes. Obrigado!
CodeManiak
36
Eu também descobri que ENOENT será lançado se você especificar cwdnas opções, mas o diretório fornecido não existe.
precisa
4
@DanielImfeld TOTAL SAVIOR. Você deve escrever uma resposta que diga isso.
GreenAsJade
4
Quando você estiver usando spawn('some-command', ['--help'], { env: env });como exemplificado pela Etapa 3 nesta resposta e estão passando um ambiente personalizado, certifique-se de especificar a PATH, por exemplo: { env: { PATH: process.env.PATH } }. A opção env não herdará variáveis ​​do seu env atual por padrão.
Anty
5
Consegui resolver meu problema passando shell: truepara as opções de desova.
Nickofthyme
35

Como @DanielImfeld apontou , ENOENT será lançado se você especificar "cwd" nas opções, mas o diretório fornecido não existir.

Leeroy Brun
fonte
11
então existe uma maneira de executar em um diretório específico o comando?
Mitro 03/10
No Windows (7) parece que você também precisa incluir a letra da unidade no cwdcaminho: 'c: / ...' e não apenas '/ ...'
Museful
29

Solução do Windows: Substitua spawnpor nó-cross-spawn . Por exemplo, no início do seu app.js:

(function() {
    var childProcess = require("child_process");
    childProcess.spawn = require('cross-spawn');
})(); 
Nilzor
fonte
2
funcionou, exceto que é um drop-in, sem a necessidade de child_process. Exatamente da mesma maneira que o spawn ou spawnSync do nó, é uma queda na substituição. var spawn = require('cross-spawn'); // Spawn NPM asynchronously var child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
Bogdan Trusca
27

A resposta de @ laconbass me ajudou e é provavelmente a mais correta.

Eu vim aqui porque estava usando o spawn incorretamente. Como um exemplo simples:

isto está incorreto:

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

isto está incorreto:

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

isto está correto:

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

no entanto, eu recomendo fazê-lo desta maneira:

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

isso ocorre porque o cp.on('exit', fn)evento sempre será acionado, desde que o bash esteja instalado; caso contrário, o cp.on('error', fn)evento poderá disparar primeiro, se o usarmos da primeira maneira, se iniciarmos o 'npm' diretamente.

Alexander Mills
fonte
11
Pensando em refatorar minha resposta para fornecer um "guia geral" e deixar detalhes para cada causa do problema (falta de dependências, chamadas incorretas, ambiente errado, ...).
precisa saber é o seguinte
2
todo mundo que gosta dessa resposta também pode se interessar por essa alternativa nativa: gist.github.com/ORESoftware/7bf225f0045b4649de6848f1ea5def4c
Alexander Mills
11
Voto negativo, porque se o que você deseja é um shell, use child_process.execou passe shell: truepara spawn.
givanse
@givanse não é necessariamente verdade - você pode querer executar zsh ou bash ou FSH, dependendo do que shell que deseja usar, e o comportamento também é diferente
Alexander Mills
22

Para ENOENT no Windows, https://github.com/nodejs/node-v0.x-archive/issues/2318#issuecomment-249355505 corrigi-lo.

por exemplo, substitua spawn ('npm', ['-v'], {stdio: 'herdar'}) por:

  • para toda a versão do node.js.

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

    spawn('npm', ['-v'], {stdio: 'inherit', shell: true})
Li Zheng
fonte
11
Onde fazer essas modificações?
Deilan
8
A parte principal está adicionandoshell: true
Ted Nyberg
19

Para qualquer um que possa se deparar com isso, se todas as outras respostas não ajudarem e você estiver no Windows, saiba que atualmente há um grande problema spawnno Windows e na PATHEXTvariável de ambiente que pode fazer com que certas chamadas sejam geradas não funcionem, dependendo de como o comando target está instalado.

Alex Turpin
fonte
2
E qual é a solução?
Nilzor
6
Usar node-cross-spawn funcionou para mim. Veja a resposta abaixo: stackoverflow.com/a/35561971/507339
Nilzor
11
Passamos idades tentando encontrar o que estava errado e isso acabou sendo o problema. Desisti spawne apenas usei exec.
reduckted
8

No meu caso, eu estava recebendo esse erro devido aos recursos necessários do sistema não estarem instalados.

Mais especificamente, tenho um aplicativo NodeJS que utiliza o ImageMagick. Apesar de ter o pacote npm instalado, o Linux ImageMagick principal não foi instalado. Eu fiz um apt-get para instalar o ImageMagick e depois disso tudo funcionou muito bem!

PromInc
fonte
O Windows também precisa do ImageMagick instalado? Testes Im nas janelas e recebendo erro
somename
6

no windows, basta adicionar a shell: trueopção resolvido meu problema:

incorreta:

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

corrigir:

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

Você está mudando a envopção?

Então olhe para esta resposta.


Eu estava tentando gerar um processo de nó e até que você deveria espalhar as variáveis ​​de ambiente existentes ao gerar, caso contrário você perderá a PATHvariável de ambiente e possivelmente outras importantes.

Esta foi a correção para mim:

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

Antes que alguém gaste muito tempo depurando esse problema, na maioria das vezes ele pode ser resolvido excluindo node_modules e reinstalando os pacotes.

Para instalar:

Se existir um arquivo de bloqueio, você poderá usar

yarn install --frozen-lockfile

ou

npm ci

respeitosamente. se não então

yarn install

ou

npm i
InsOp
fonte
Uau, uma solução tão simples e funcionou para mim! Todos devem tentar isso primeiro para ver se resolve o problema.
Nick K
2

Encontrei o mesmo problema, mas encontrei uma maneira simples de corrigi-lo. Parece haver spawn()erros se o programa foi adicionado ao PATH pelo usuário (por exemplo, comandos normais do sistema funcionam).

Para corrigir isso, você pode usar o que módulo ( 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
fonte
2

Use em require('child_process').execvez de gerar para obter uma mensagem de erro mais específica!

por exemplo:

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

Certifique-se de que o módulo a ser executado esteja instalado ou o caminho completo para o comando, se não for um módulo de nó

Dalton
fonte
1

Eu também estava passando por esse problema irritante enquanto executava meus casos de teste, então tentei várias maneiras de contorná-lo. Mas a maneira que funciona para mim é executar o executor de teste no diretório que contém o arquivo principal, que inclui a função de geração nodejs , algo como isto:

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

Por exemplo, esse nome de arquivo é test.js , então vá para a pasta que o contém . No meu caso, é uma pasta de teste como esta:

cd root/test/

em seguida, execute o seu executor de teste no meu caso, seu mocha, para que fique assim:

mocha test.js

Eu desperdicei meu mais de um dia para descobrir isso. Aproveitar!!

Rajkumar Bansal
fonte
1

Eu encontrei esse problema no Windows, onde chamar exece spawncom exatamente o mesmo comando (omitindo argumentos) funcionava bem exec(então eu sabia que meu comando estava ativado $PATH), mas spawndaria ENOENT. Aconteceu que eu só precisava acrescentar .exeao comando que estava 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']);
MostlyArmless
fonte
0

Eu estava recebendo esse erro ao tentar depurar um programa node.js no editor de código VS em um sistema Debian Linux. Notei que a mesma coisa funcionou bem no Windows. As soluções fornecidas anteriormente aqui não ajudaram muito, porque eu não havia escrito nenhum comando "spawn". O código incorreto foi presumivelmente escrito pela Microsoft e oculto sob o capô do programa VS Code.

Em seguida, notei que o node.js é chamado de nó no Windows, mas no Debian (e provavelmente em sistemas baseados no Debian, como o Ubuntu), é chamado de nodejs. Então eu criei um alias - a partir de um terminal raiz, corri

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

e isso resolveu o problema. O mesmo procedimento ou um procedimento semelhante provavelmente funcionará em outros casos em que o node.js é chamado nodejs, mas você está executando um programa que espera que seja chamado de node ou vice-versa.

MTGradwell
fonte
0

Se você estiver no Windows, o Node.js faz alguma coisa engraçada ao manipular cotações que podem resultar na emissão de um comando que você sabe que funciona no console, mas não quando executado no Node. Por exemplo, o seguinte deve funcionar:

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

mas falha. Existe uma opção fantasticamente não documentada windowsVerbatimArgumentspara lidar com aspas / similar que parece funcionar, basta adicionar o seguinte ao objeto opts:

const opts = {
    windowsVerbatimArguments: true
};

e seu comando deve estar de volta aos negócios.

 spawn('ping', ['"8.8.8.8"'], { windowsVerbatimArguments: true });
Joel B
fonte
Não cite os argumentos dentro da matriz
laconbass
@laconbass Este é um exemplo obviamente trivial para transmitir o conceito e, portanto, as aspas podem ser removidas. No entanto, há casos em que você absolutamente precisa citar os argumentos (por exemplo, se precisar passar um argumento que tenha um caminho com um espaço nele: "C: \ Arquivos de Programas \ ..." ). Publiquei aqui porque, mesmo que não tenha sido a causa do seu caso de erro específico, espero ajudar alguém a experimentar esse erro enigmático por causa do tratamento de cotações do Node no Windows como eu estava encontrando.
Joel B
O node.js já faz alguns Black Magic e silenciosamente aspas argumentos "corretamente". Seu exemplo deve funcionar sem a opção não documentada mencionada, citando argumentos dentro da matriz.
laconbass
Apenas para adicionar minha própria experiência, eu estava executando um processo java a partir do nó. Esse erro aconteceu comigo por causa de aspas ao redor do comando, e não do argumento. Teste com espaços no caminho de comando e ainda funciona sem aspas
Troncoso
0

solução no meu 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
fonte
11
Embora isso possa ser uma solução para correções de vitória específicos, não vejo como ele ajuda a depurar a causa real do ENOENT
laconbass
Não tenho idéia do porquê, mas a chamada de spawn funcionaria na substituição do nó sem o .cmd, mas falharia em um teste de brincadeira datilografado. - Esse erro pode ser bastante difícil de descobrir, essas respostas merecem mais votos.
Mathieu CAROFF
0

Caso esteja enfrentando esse problema com um aplicativo cuja fonte não pode ser modificada, considere invocá-lo com a variável de ambiente NODE_DEBUGdefinida como child_process, por exemplo NODE_DEBUG=child_process yarn test. Isso fornecerá informações sobre quais linhas de comando foram chamadas em qual diretório e, geralmente, o último detalhe é o motivo da falha.

Karl Richter
fonte
0

Embora possa ser um caminho do ambiente ou outro problema para algumas pessoas, eu havia acabado de instalar a extensão do Latex Workshop para o Visual Studio Code no Windows 10 e vi esse erro ao tentar criar / visualizar o PDF. A execução do código VS como administrador resolveu o problema para mim.

Steve
fonte
11
Novamente, o caminho do sistema de arquivos está relacionado de alguma maneira. A extensão provavelmente não pode alcançar um caminho sem permissões de administrador
laconbass
-1

O problema é por causa de uma variável de ambiente do caminho do sistema estar ausente. Adicione o valor "C: \ Windows \ System32 \" à variável PATH do sistema.

Chaya Sandamali
fonte
-2

Adicione C:\Windows\System32\à pathvariável de ambiente.

Passos

  1. Vá para o meu computador e propriedades

  2. Clique em Configurações avançadas

  3. Em seguida, nas variáveis ​​de ambiente

  4. Selecione Pathe clique em editar

  5. Cole o seguinte, se ainda não estiver presente: C:\Windows\System32\

  6. Feche o prompt de comando

  7. Execute o comando que você queria executar

Captura de tela das variáveis ​​de ambiente do Windows 8

vmit dhawan
fonte
3
Esta é uma duplicata da resposta
Emile Bergeron