node icon indicating copy to clipboard operation
node copied to clipboard

Cannot exec or spawn node, npm or yarn [node snap]

Open fcole90 opened this issue 3 years ago • 7 comments

  • Version: v14.16.0
  • Platform: Linux fabio-XPS-15-7590 5.4.0-70-generic #78-Ubuntu SMP Fri Mar 19 13:29:52 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
  • Subsystem: child_process
  • Distributed as: snap

What steps will reproduce the bug?

Install node snap: sudo snap install node --classic

Optional: fix permission issues https://npm.github.io/installation-setup-docs/installing/a-note-on-permissions.html

Then run the following:

const { exec } = require('child_process');

// Test
exec('echo "exec test is running"', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec test error: ${error}`);
    return;
  }
  console.log(`exec test stdout: ${stdout}`);
  console.error(`exec test stderr: ${stderr}`);
});

// Node
exec('node -v', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec node error: ${error}`);
    return;
  }
  console.log(`exec node stdout: ${stdout}`);
  console.error(`exec node stderr: ${stderr}`);
});

// NPM
exec('npm --version', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec npm error: ${error}`);
    return;
  }
  console.log(`exec npm stdout: ${stdout}`);
  console.error(`exec npm stderr: ${stderr}`);
});

// Yarn
exec('yarn --version', (error, stdout, stderr) => {
  if (error) {
    console.error(`exec yarn error: ${error}`);
    return;
  }
  console.log(`exec yarn stdout: ${stdout}`);
  console.error(`exec yarn stderr: ${stderr}`);
});

// -------------------------------------------------------

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

// Test
const testSpawn = spawn('echo', ['"test spawn is running"']);
testSpawn.stdout.on('data', (data) => {
  console.log(`spawn test stdout: ${data}`);
});
testSpawn.stderr.on('data', (data) => {
  console.error(`spawn test stderr: ${data}`);
});
testSpawn.on('close', (code) => {
  console.log(`spawn test child process exited with code ${code}`);
});

// Node
const nodeSpawn = spawn('node', ['-v']);
nodeSpawn.stdout.on('data', (data) => {
  console.log(`spawn node stdout: ${data}`);
});
nodeSpawn.stderr.on('data', (data) => {
  console.error(`spawn node stderr: ${data}`);
});
nodeSpawn.on('close', (code) => {
  console.log(`spawn node child process exited with code ${code}`);
});

// NPM
const npmSpawn = spawn('npm', ['--version']);
npmSpawn.stdout.on('data', (data) => {
  console.log(`spawn npm stdout: ${data}`);
});
npmSpawn.stderr.on('data', (data) => {
  console.error(`spawn npm stderr: ${data}`);
});
npmSpawn.on('close', (code) => {
  console.log(`spawn npm child process exited with code ${code}`);
});

// Yarn
const yarnSpawn = spawn('yarn', ['--version']);
yarnSpawn.stdout.on('data', (data) => {
  console.log(`spawn yarn stdout: ${data}`);
});
yarnSpawn.stderr.on('data', (data) => {
  console.error(`spawn yarn stderr: ${data}`);
});
yarnSpawn.on('close', (code) => {
  console.log(`spawn yarn child process exited with code ${code}`);
});

How often does it reproduce? Is there a required condition?

It always happens to me. I think a requirement to be using node from the snap.

What is the expected behaviour?

$ node index.js 
spawn test stdout: "test spawn is running"

spawn test child process exited with code 0
exec test stdout: exec test is running

exec test stderr: 
exec node stdout: v14.16.0
exec node stderr: 
spawn node stdout: v14.16.0
spawn node child process exited with code 0
spawn yarn stdout: 1.22.5
spawn yarn child process exited with code 0
exec yarn stdout: 1.22.5
exec yarn stderr: 

exec npm stdout: 7.7.5
exec npm stderr: 
spawn npm stdout: 7.7.5
spawn npm child process exited with code 0

What do you see instead?

$ node index.js
spawn test stdout: "test spawn is running"

spawn test child process exited with code 0
exec test stdout: exec test is running

exec test stderr: 
exec node stdout: 
exec node stderr: 
spawn node child process exited with code 0
spawn yarn child process exited with code 1
exec yarn error: Error: Command failed: yarn --version

exec npm stdout: 
exec npm stderr: 
spawn npm child process exited with code 0

The output of node and npm commands is empty, while yarn fails.

However, running each of those commands from the terminal works correctly:

$ node -v
v14.16.0
$ npm --version
7.7.5
$ yarn --version
1.22.5

Additional information

Running a command like which npm returns the correct binary path both from the terminal and from the script. I also attempted to write the full path in the script, instead of the command, (e.g. /home/fabio/.npm-global/bin/npm) but nothing changes.

The node binary is provided by the node snap. However, I could not find clear issues in the logs. Also npm and yarn are provided in the snap, but I installed them globally under /home/fabio/.npm-global.

✅ I'm willing to help with debugging and getting this solved. 👨‍💻

fcole90 avatar Mar 30 '21 08:03 fcole90

I mentioned this issue in the snap forum, so we might receive some special assistance from snap experts: https://forum.snapcraft.io/t/node-snap-issues-with-exec-of-npm-node-yarn/23635

fcole90 avatar Mar 30 '21 09:03 fcole90

Can you try to run the following commands in your terminal:

/bin/sh -c 'node -v'
/bin/sh -c 'npm --version'
/bin/sh -c 'yarn --version'

aduh95 avatar Mar 30 '21 09:03 aduh95

Sure, here you are:

$ /bin/sh -c 'node -v'
v14.16.0
$ /bin/sh -c 'npm --version'
7.7.5
$ /bin/sh -c 'yarn --version'
1.22.5

fcole90 avatar Mar 30 '21 10:03 fcole90

Possibly related issues:

  • https://bugs.launchpad.net/ubuntu/+source/snapd/+bug/1849753
  • https://github.com/snapcore/snapd/pull/10029

fcole90 avatar Mar 30 '21 10:03 fcole90

https://github.com/microsoft/vscode-vsce/issues/341 was recently closed, apparently owing to this or a similar problem with child processes. Mentioning here.

iamjameswalters avatar Aug 31 '21 14:08 iamjameswalters

Hi! We installed Node.js via snap store and tried to use exec and spawn functions in the VS Code extension environment. In the test extension we could also reproduce the same incorrect behavior as described in this issue. The functions for spawning child processes skip the execution of npm and CLI commands (based on Node.js scripts) and return just an empty result or an error.

etatanova avatar Sep 24 '21 12:09 etatanova

I am getting same error when doing spawn('yarn build'). code: 'ENOENT'. And getting same error on running any node binaries (webpack, npm etc). System commands (like curl, zip) are working fine.

I tried doing which yarn and which node, yarn is picked from some tmp file, and node is also running from tmp yarn folder.

When I run the script directly (instead of via yarn), it returns the node inside the nvm directory path, but still not working.

Interestingly which yarn and which node are working fine.

piyushkmr avatar Aug 02 '22 04:08 piyushkmr

I looked into this.

EDIT: Correction:

node cannot run snap due to sandbox limitations. This blocks node from executing some snap programs as children.

on the other hand, running node in the true path (in my case /snap/node/6694/bin/node currently) will work. causing things like a npm version check to work like this (as long as snap is installed, in which case "which" command is also present): execSync(process.execPath + ' $(which npm) --version').toString().trim() (returns the correct 8.19.2 in my case)

otherwise the command execSync('npm --version') will return an empty string, because "snap" just terminates itself before printing anything.

================== Edit End ========= as noticed, node cannot spawn a child of any given snap program. This is caused by the "symlink" structure of the bin files in /snap/bin

ll /snap/bin reveals that all of those link to /usr/bin/snap in one way or another. I have no idea how on earth the bash knows how to execute them. I guess there is an env variable which does contain this info, mapping the execution of the snap bin to snap run [bin-name]

but this means, whenever a which [snap-program-name] resolves to /snap/bin/[prog-name] it must be run snap run [name] instead of calling it from terminal directly. The bash seems to notice the weird symlink, and knows what to do. But node exec does not. (understandable, i have no idea either xD) Anyway. There should be a solution to this.

Bloodiko avatar Sep 15 '22 16:09 Bloodiko

@Bloodiko great work looking into this! :blush:

fcole90 avatar Sep 16 '22 08:09 fcole90

In the meanwhile the following workaround should help (works on Ubuntu 22.04):

for cmd in node npm yarn; do

mkdir -p ~/bin

cat > "$HOME/bin/$cmd" << EOF
#!/bin/bash

# https://github.com/nodejs/node/issues/37982
/snap/bin/$cmd "\$@" > >(cat) 2> >(cat >&2)
EOF

chmod +x "$HOME/bin/$cmd"

done

giner avatar Mar 02 '23 05:03 giner

I'll go ahead and close this because it's a snap sandbox restriction, not an issue with node itself. If the restriction is a problem for you, you should report it to the maintainers of the snap package.

bnoordhuis avatar Mar 04 '23 11:03 bnoordhuis