Summary
When a Node.js script uses #!/usr/bin/env node and is spawned as a subprocess, the snap wrapper at /snap/bin/node silently exits with code 7 (no stderr output). The identical script works correctly when invoked with /snap/node/current/bin/node directly.
Environment
|
|
| OS |
Ubuntu 25.10 (kernel 6.17.0-23-generic, amd64) |
| snap node |
22.22.3 (22/stable, rev 11560) |
| snapd |
2.75.2 |
| `/snap/bin/node` |
symlink → `/usr/bin/snap` |
| `/snap/node/current` |
symlink → `11560` |
Steps to reproduce
# Works fine — direct binary
timeout 5 /snap/node/current/bin/node /path/to/pyright/langserver.index.js --stdio </dev/zero
# exit 124 (timeout), pyright LSP output received on stdout
# Fails — snap wrapper
timeout 5 /snap/bin/node /path/to/pyright/langserver.index.js --stdio </dev/zero
# exit 7, no output at all
The failure also manifests whenever a script with #!/usr/bin/env node is spawned as a subprocess and /snap/bin is the first node entry in PATH — e.g., the pyright-langserver language server script launched by editor tooling.
What /snap/bin/node does
strace -e trace=execve /snap/bin/node --version shows the wrapper performing a double execve:
execve("/snap/bin/node", [...], /* 110 vars */) = 0
execve("/snap/snapd/current/usr/bin/snap", ["/snap/bin/node", "--version"], /* 110 vars */) = 0
This chain works when called interactively. It fails silently (exit 7) when the wrapper is the shebang interpreter of a subprocess whose parent process is not a full login shell.
Impact
Any tool that relies on #!/usr/bin/env node and is spawned as a subprocess (language servers, build tools, test runners) will silently fail on systems where snap node's /snap/bin/node is the first node in PATH.
Observed with: pyright-langserver (pyright 1.1.409) launched by Claude Code's pyright-lsp plugin.
Workaround
Create a thin wrapper earlier in PATH that delegates to the direct binary:
# ~/.local/bin/node
#!/bin/bash
exec /snap/node/current/bin/node "$@"
Expected behaviour
/snap/bin/node should behave identically to /snap/node/current/bin/node in all execution contexts, including as a shebang interpreter invoked by a subprocess.
Summary
When a Node.js script uses
#!/usr/bin/env nodeand is spawned as a subprocess, the snap wrapper at/snap/bin/nodesilently exits with code 7 (no stderr output). The identical script works correctly when invoked with/snap/node/current/bin/nodedirectly.Environment
Steps to reproduce
The failure also manifests whenever a script with
#!/usr/bin/env nodeis spawned as a subprocess and/snap/binis the firstnodeentry inPATH— e.g., thepyright-langserverlanguage server script launched by editor tooling.What
/snap/bin/nodedoesstrace -e trace=execve /snap/bin/node --versionshows the wrapper performing a doubleexecve:This chain works when called interactively. It fails silently (exit 7) when the wrapper is the shebang interpreter of a subprocess whose parent process is not a full login shell.
Impact
Any tool that relies on
#!/usr/bin/env nodeand is spawned as a subprocess (language servers, build tools, test runners) will silently fail on systems where snap node's/snap/bin/nodeis the firstnodein PATH.Observed with:
pyright-langserver(pyright 1.1.409) launched by Claude Code's pyright-lsp plugin.Workaround
Create a thin wrapper earlier in PATH that delegates to the direct binary:
Expected behaviour
/snap/bin/nodeshould behave identically to/snap/node/current/bin/nodein all execution contexts, including as a shebang interpreter invoked by a subprocess.