-
-
Notifications
You must be signed in to change notification settings - Fork 33.4k
Open
Description
Version
v22.19.0
Platform
Darwin <hostname> 25.0.0 Darwin Kernel Version 25.0.0: Wed Sep 17 21:41:50 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T6030 arm64
Subsystem
child_process
What steps will reproduce the bug?
Install ripgrep
(https://github.com/BurntSushi/ripgrep) — just a example of something that breaks this way.
run the following and it will time out:
const { execFile } = await import("node:child_process")
const child = execFile("rg", ["foo"], {
stdio: ["ignore", "pipe", "pipe"],
timeout: 1_000
}, (error, _stdout, _stderr) => {
console.log("After one second:", error)
})
You will see:
❯ node
Welcome to Node.js v22.19.0.
Type ".help" for more information.
> const { execFile } = await import("node:child_process")
undefined
> const child = execFile("rg", ["foo"], {
... stdio: ["ignore", "pipe", "pipe"],
... timeout: 1_000
... }, (error, _stdout, _stderr) => {
... console.log("After one second:", error)
... })
undefined
> After one second: Error: Command failed: rg foo
at genericNodeError (node:internal/errors:983:15)
at wrappedFn (node:internal/errors:537:14)
at ChildProcess.exithandler (node:child_process:417:12)
at ChildProcess.emit (node:events:519:28)
at ChildProcess.emit (node:domain:552:15)
at maybeClose (node:internal/child_process:1101:16)
at ChildProcess._handle.onexit (node:internal/child_process:304:5)
at Process.callbackTrampoline (node:internal/async_hooks:130:17) {
code: null,
killed: true,
signal: 'SIGTERM',
cmd: 'rg foo'
}
How often does it reproduce? Is there a required condition?
With any command (like rg
) that could hang on an open stdin
. See: BurntSushi/ripgrep#2056
What is the expected behavior? Why is that the expected behavior?
For exec
and execFile
to allow setting stdin
to ignore
— setting stdout
and stderr
is irrelevant as they are gathered by exec{,File}
itself. The current implementation simply has no way to set this:
Lines 301 to 326 in 31c6e70
/** | |
* Spawns the specified file as a shell. | |
* @param {string} file | |
* @param {string[]} [args] | |
* @param {{ | |
* cwd?: string | URL; | |
* env?: Record<string, string>; | |
* encoding?: string; | |
* timeout?: number; | |
* maxBuffer?: number; | |
* killSignal?: string | number; | |
* uid?: number; | |
* gid?: number; | |
* windowsHide?: boolean; | |
* windowsVerbatimArguments?: boolean; | |
* shell?: boolean | string; | |
* signal?: AbortSignal; | |
* }} [options] | |
* @param {( | |
* error?: Error, | |
* stdout?: string | Buffer, | |
* stderr?: string | Buffer | |
* ) => any} [callback] | |
* @returns {ChildProcess} | |
*/ | |
function execFile(file, args, options, callback) { |
What do you see instead?
Simply hangs.
Additional information
Shout out to my teammate @Kaushal1011 who figured out why this got stuck!
Kaushal1011
Metadata
Metadata
Assignees
Labels
No labels