Version
All versions, tested on v12 through v23.6.0
Platform
Windows 10 and 11
What steps will reproduce the bug?
Launching a process tree like the following causes grandchild2 to hang (cannot attach Node inspector, no JS code executes) after calling import('process') (which never resolves, unless its sibling grandchild1 exits):
main.js - launches child with piped stdin
middle.js - launches children with inherited stdin
grandchild1.js - attaches a listener to stdin
(1s later) grandchild2.js - imports node:process and then hangs
Example code:
main.js
require('child_process').spawn('node', ['middle.js'], { stdio: ['pipe', 'inherit', 'inherit'] });
middle.js
const { spawn } = require('child_process');
spawn('node', ['grandchild1.js'], { stdio: 'inherit' });
// Note: this also works: spawn('cmd', ['/c', 'pause'], { stdio: 'inherit' });
setTimeout(function () {
spawn('node', ['grandchild2.js'], { stdio: 'inherit' });
}, 1000);
grandchild1.js
process.stdin.on('data', console.log);
grandchild2.js
import('process').then(function (mod) {
console.log('import(node:process) completed'); // this line is never executed
});
console.log(process.pid, `grandchild2 started - if you don't see "not stuck" below, it's stuck`);
setInterval(function () {
console.log(process.pid, 'not stuck');
}, 1000);
Example repo with the above code (plus a bit more logging): https://github.com/Jimbly/node-stdin-hang-bug-demo
Tested on (all fail):
- Node v12...v22
- Windows 10, 11
Works fine on Linux, MacOS, presumably all non-Windows platforms.
How often does it reproduce? Is there a required condition?
Always (on Windows).
Additional information
Hang does not occur if any of these happen:
grandchild1 is launched before grandchild2
middle's stdin is not piped to main
grandchild1's or grandchild2's stdin is not inherit
grandchild1 exits (causes grandchild2 to get unstuck)
Hang still occurs if:
grandchild1 is anything that reads from stdio (e.g. cmd /c pause)
The hung process's (native) call stack is:
NtSetInformationFile()
SetNamedPipeHandleState(pipeHandle, PIPE_READMODE_BYTE | PIPE_WAIT)
node.exe!uv__set_pipe_handle(loop=0x00007ff71d27f130, handle=0x000001c0c83367f0, pipeHandle=0x0000000000000278, fd=-1, duplex_flags=49152)
at deps\uv\src\win\pipe.c(482)
node.exe!uv_pipe_open(pipe=0x000001c0c83367f0, file=0)
at deps\uv\src\win\pipe.c(2480)
node.exe!node::PipeWrap::Open(args={...})
at src\pipe_wrap.cc(209)
node.exe!Builtins_CallApiCallbackGeneric()
at out\Release\obj\v8_snapshot\embedded.S(4265)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_InterpreterPushArgsThenFastConstructFunction()
at out\Release\obj\v8_snapshot\embedded.S(4038)
node.exe!Builtins_ConstructHandler()
at out\Release\obj\v8_snapshot\embedded.S(56237)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_GetPropertyWithReceiver()
at out\Release\obj\v8_snapshot\embedded.S(24522)
node.exe!Builtins_ReflectGet()
at out\Release\obj\v8_snapshot\embedded.S(41510)
00007ff69938aaf7()
00007ff69938acf4()
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_JSEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3658)
node.exe!Builtins_JSEntry()
at out\Release\obj\v8_snapshot\embedded.S(3616)
[Inline Frame] node.exe!v8::internal::GeneratedCode<unsigned __int64,unsigned __int64,unsigned __int64,unsigned __int64,unsigned __int64,__int64,unsigned __int64 * *>::Call()
at deps\v8\src\execution\simulator.h(178)
node.exe!v8::internal::`anonymous namespace'::Invoke(isolate=0x000001c0c82c1000, params={...})
at deps\v8\src\execution\execution.cc(420)
node.exe!v8::internal::Execution::Call(isolate=0x000001c0c82c1000, callable, receiver, argc=0, argv=0x0000000000000000)
at deps\v8\src\execution\execution.cc(506)
node.exe!v8::Function::Call(context, recv={...}, argc=0, argv=0x0000000000000000)
at deps\v8\src\api\api.cc(5484)
node.exe!node::loader::ModuleWrap::SyntheticModuleEvaluationStepsCallback(context={...}, module)
at src\module_wrap.cc(948)
node.exe!v8::internal::SyntheticModule::Evaluate(isolate=0x000001c0c82c1000, module={...})
at deps\v8\src\objects\synthetic-module.cc(108)
node.exe!v8::internal::Module::Evaluate(isolate=0x000001c0c82c1000, module={...})
at deps\v8\src\objects\module.cc(284)
node.exe!v8::Module::Evaluate(context)
at deps\v8\src\api\api.cc(2461)
[Inline Frame] node.exe!node::loader::ModuleWrap::Evaluate::__l2::<lambda_1>::operator()()
at src\module_wrap.cc(562)
node.exe!node::loader::ModuleWrap::Evaluate(args={...})
at src\module_wrap.cc(578)
node.exe!Builtins_CallApiCallbackGeneric()
at out\Release\obj\v8_snapshot\embedded.S(4265)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_AsyncFunctionAwaitResolveClosure()
at out\Release\obj\v8_snapshot\embedded.S(11765)
node.exe!Builtins_PromiseFulfillReactionJob()
at out\Release\obj\v8_snapshot\embedded.S(40771)
node.exe!Builtins_RunMicrotasks()
at out\Release\obj\v8_snapshot\embedded.S(9703)
(truncated for size, see above repo if more stack is useful)
Version
All versions, tested on v12 through v23.6.0
Platform
Windows 10 and 11
What steps will reproduce the bug?
Launching a process tree like the following causes
grandchild2to hang (cannot attach Node inspector, no JS code executes) after callingimport('process')(which never resolves, unless its siblinggrandchild1exits):Example code:
main.js
middle.js
grandchild1.js
grandchild2.js
Example repo with the above code (plus a bit more logging): https://github.com/Jimbly/node-stdin-hang-bug-demo
Tested on (all fail):
Works fine on Linux, MacOS, presumably all non-Windows platforms.
How often does it reproduce? Is there a required condition?
Always (on Windows).
Additional information
Hang does not occur if any of these happen:
grandchild1is launched beforegrandchild2middle'sstdinis notpiped tomaingrandchild1's orgrandchild2'sstdinis notinheritgrandchild1exits (causesgrandchild2to get unstuck)Hang still occurs if:
grandchild1is anything that reads from stdio (e.g.cmd /c pause)The hung process's (native) call stack is: