Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] npm/npx crashes if stdout is Node's 'pipe' #2114

Closed
kf6kjg opened this issue Nov 3, 2020 · 7 comments
Closed

[BUG] npm/npx crashes if stdout is Node's 'pipe' #2114

kf6kjg opened this issue Nov 3, 2020 · 7 comments
Labels
Bug thing that needs fixing cmd:exec related to `npx` Priority 2 secondary priority issue Release 7.x work is associated with a specific npm 7 release

Comments

@kf6kjg
Copy link

kf6kjg commented Nov 3, 2020

Current Behavior

Executing npm or npx commands from within a parent process that sets the child's stdout to "pipe" causes npm/npx to crash.

Expected Behavior

npm/npx to detect that this condition exists and proceed without issue.

Steps To Reproduce

  1. Using Linux with Node 12 in a folder containing a valid package.json.

  2. With rimraf already installed as a (optionally dev) dependency. I chose rimraf for this example as it is well-known and simple, but this reproduces with any command. I was originally having the problem with husky-run.

  3. Execute the following:

    echo 'const child = require("child_process"); child.execSync("npx --no-install rimraf x");' | node -

    or any of the following

    echo 'const child = require("child_process"); child.execSync("npx --no-install rimraf x", {stdio:"pipe"});' | node -
    echo 'const child = require("child_process"); child.execSync("npx --no-install rimraf x", {stdio:["ignore", "pipe"]});' | node -

    results in

    child_process.js:674
        throw err;
        ^
    
    Error: Command failed: npx rimraf x
        at checkExecSyncError (child_process.js:635:11)
        at Object.execSync (child_process.js:671:15)
        at [stdin]:1:47
        at Script.runInThisContext (vm.js:120:18)
        at Object.runInThisContext (vm.js:309:38)
        at Object.<anonymous> ([stdin]-wrapper:10:26)
        at Module._compile (internal/modules/cjs/loader.js:1015:30)
        at evalScript (internal/process/execution.js:94:25)
        at internal/main/eval_stdin.js:29:5
        at Socket.<anonymous> (internal/process/execution.js:207:5) {
      status: 243,
      signal: null,
      output: [ null, Buffer(0) [Uint8Array] [], Buffer(0) [Uint8Array] [] ],
      pid: 247932,
      stdout: Buffer(0) [Uint8Array] [],
      stderr: Buffer(0) [Uint8Array] []
    }
    

Workarounds

echo 'const child = require("child_process"); child.execSync("npx --no-install rimraf x", {stdio:"inherit"});' | node -
echo 'const child = require("child_process"); child.execSync("npx --no-install rimraf x", {stdio:"ignore"});' | node -
echo 'const child = require("child_process"); child.execSync("npx --no-install rimraf x", {stdio:[0,1]});' | node -
echo 'const child = require("child_process"); child.execSync("npx --no-install rimraf x", {stdio:[undefined, "ignore"]});' | node -

Environment

  • OS: Alpine 3.11.6 and Ubuntu 20.04
  • Node: 14.2.0 and 12.19.0, respectively
  • npm: tested on both 6.14.4 and 7.0.7, respectively

Under Alpine's Node 14 I was unable to reproduce, likely due to Node having changed behavior. These two configurations are what I have at hand. I did first detect the issue on my Ubuntu 20.04 with Node 12 and npm 6 setup before I upgraded npm to 7.

Notes

This fundamental problem is the source of several bug reports over the years in many repositories, so I have no doubt that an issue exists that already documents this bug - but I've been unable to find it.

@kf6kjg kf6kjg added Bug thing that needs fixing Needs Triage needs review for next steps Release 7.x work is associated with a specific npm 7 release labels Nov 3, 2020
@isaacs
Copy link
Contributor

isaacs commented Nov 11, 2020

This works for me on Darwin and Linux, using npm 6.14.2, 6.14.8, 7.0.7, and v7.0.10, with node 14.2.0, 14.15.0, and 15.2.0.

$ mkdir -p x/y/z

$ echo 'const child = require("child_process"); child.execSync("npx --no-install rimraf x", {stdio:"pipe"});' | node -

$ tree x
"x": No such file or directory (os error 2)

When rimraf isn't installed locally or in the npx cache, the --no-install causes it to fail with the cancelled error, but that's expected.

$ echo 'const child = require("child_process"); child.execSync(`npx --no-install rimraf x`, {stdio:"pipe"});' | node -
node:child_process:675
    throw err;
    ^

Error: Command failed: npx --no-install rimraf x
npm ERR! canceled

I'm not sure why you'd be getting an empty error output though. Does it create a debug log file in ~/.npm somewhere? Can you run other commands (other than npx) in this manner? Does it work if you remove the --no-install argument, or if you first run it without the subshell? Does it work if you delete ~/.npm/_npx?

@kf6kjg
Copy link
Author

kf6kjg commented Nov 12, 2020

Good testing @isaacs, however as mentioned in the original description this doesn’t repro on Node 14 or newer. I’ve not tested 13. It does happen under Node 12 - at least on my machine. And while I’d love to be running 14 and getting access to all the niceness it brings, several projects aren’t paying for that upgrade yet. 

As to your questions, again running under Ubuntu and Node 12 with NPM 7.

  1. I'm not sure why you'd be getting an empty error output though.
    • I'm unsure what you mean by an "empty error output" unless you mean that the streams were empty?
  2. Does it create a debug log file in ~/.npm somewhere?
  3. Can you run other commands (other than npx) in this manner?
    • My memory indicates that the npm command behaved similarly, but I'm currently not repro'ing with npm. Common shell commands such as ls and find don't error out.
  4. Does it work if you remove the --no-install argument, or if you first run it without the subshell?
    • Under npm v6 and v7 there are different behaviors when a package isn't in the package.json and npx is called without the --no-install flag. Using the flag makes both versions behave similarly.
    • I'm unsure what you mean by "if you first run it without the subshell" as I'm not aware of a subshell being used in the example I gave.
  5. Does it work if you delete ~/.npm/_npx?
    • Behavior doesn't change.

@kf6kjg
Copy link
Author

kf6kjg commented Nov 12, 2020

As an experiment I upgraded my node to 14:

$ snap refresh --channel=14/stable node
node (14/stable) 14.14.0 from OpenJS Foundation (iojs✓) refreshed
$ node -v
v14.14.0
$ npm -v
7.0.7

And re-ran the test. So the following combination is failing the test command on my machine:
Ubuntu 20.04, node 14.14.0, npm 7.0.7.

$ mkdir -p x/y/z
$ ls -ld x
drwxrwxr-x 3 ricky ricky 4096 Nov 12 11:29 x
$ echo 'const child = require("child_process"); child.execSync("npx --no-install rimraf x", {stdio:"pipe"});' | node -
child_process.js:655
    throw err;
    ^

Error: Command failed: npx --no-install rimraf x
    at checkExecSyncError (child_process.js:616:11)
    at Object.execSync (child_process.js:652:15)
    at [stdin]:1:47
    at Script.runInThisContext (vm.js:132:18)
    at Object.runInThisContext (vm.js:309:38)
    at internal/process/execution.js:77:19
    at [stdin]-wrapper:6:22
    at evalScript (internal/process/execution.js:76:60)
    at internal/main/eval_stdin.js:29:5
    at Socket.<anonymous> (internal/process/execution.js:198:5) {
  status: 243,
  signal: null,
  output: [ null, Buffer(0) [Uint8Array] [], Buffer(0) [Uint8Array] [] ],
  pid: 3154622,
  stdout: Buffer(0) [Uint8Array] [],
  stderr: Buffer(0) [Uint8Array] []
}
$ ls -ld x
ls: cannot access 'x': No such file or directory

With the following log:
2020-11-12T19_28_03_514Z-debug.log

@darcyclarke darcyclarke added Priority 2 secondary priority issue platform: linux is Linux specific and removed Needs Triage needs review for next steps labels Jan 22, 2021
@darcyclarke darcyclarke added this to the OSS - Sprint 23 milestone Jan 25, 2021
@kf6kjg
Copy link
Author

kf6kjg commented Feb 8, 2021

More fun that might, or might not, help locate the origin of this issue: today I was trying to do some bash scripting around some npm commands and got a very similar interesting result:

$ npm run build; echo $?
# <SNIP, runs fine>
0
$ npm run build 2> test; echo $?; cat test
243

There was no content in the test file.

The relevant line from package.json is "build": "react-scripts build" - so pretty standard.

This repros in both of the following configurations on Ubuntu 20.04.2 LTS, with node and npm installed via the official snap package:

  • Node v12.19.0, NPM 6.14.8
  • Node v14.15.4, NPM 6.14.10

Kinda looks like it really doesn't like being redirected away from a tty.

@darcyclarke darcyclarke removed the platform: linux is Linux specific label Feb 24, 2021
@darcyclarke
Copy link
Contributor

@kc15155 I wasn't able to repro this on Node v10.24.0 or v12.21.0 w/ npm v6.14.11 or v7.5.6 on Darwin - potentially try removing the --no-install (which is deprecated in v7 either way - it falls back to --no which is the equivelant of --yes=false - ref. https://docs.npmjs.com/cli/v7/commands/npm-exec#description & https://docs.npmjs.com/cli/v7/commands/npm-exec#compatibility-with-older-npx-versions) & maybe try setting the rimraf args directly, avoiding npm's parsing altogether (ex. echo 'const child = require("child_process"); child.execSync("npx rimraf -- x", {stdio:"pipe"});' | node -) - I also noticed in your debug logs some EACCES errors which may also indicate some permissions issues but lets try the above first before we dig in on that.

@kf6kjg
Copy link
Author

kf6kjg commented Feb 24, 2021

The point here isn't rimraf. I chose rimraf because it was simple and common. The point is that, under the conditions I specified, running npm or npx with the stderr routed to anything that's not a tty, like using it from node, things don't behave.

@fritzy fritzy added the cmd:exec related to `npx` label Jul 25, 2022
@wraithgar
Copy link
Member

This appears to have been fixed at least as of npm@8.16.0

~/D/n/s/fresh4 $ npx @gar/create-test > test; echo $status; cat test
0
Gar's test create script version 2.0.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug thing that needs fixing cmd:exec related to `npx` Priority 2 secondary priority issue Release 7.x work is associated with a specific npm 7 release
Projects
None yet
Development

No branches or pull requests

5 participants