You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
unixTerminal: input is silently queued forever when libuv threadpool completions stop arriving (CustomWriteStream has no watchdog) #939
Electron app (pty host runs in a utility process), macOS 15 / darwin arm64
Issue
CustomWriteStream.write() queues data and relies on the async fs.write completion callback to advance the queue:
write() only starts processing when the queue transitions 0 → 1
_processWriteQueue() issues one fs.write and continues from its callback
fs.write completes on the libuv threadpool. If the pool stops delivering completions — which we observed live in production: all 4 workers idle in pthread_cond_wait, submitted work never picked up, event loop perfectly healthy — the callback never fires, the queue head never advances, and every subsequent write() appends to the queue forever. There is no error, no timeout, and no log: from the embedder's perspective the terminal simply stops accepting input while output (event-loop reads) keeps flowing. Terminal.write() returns void, so the embedder cannot observe or mitigate this from outside.
We confirmed this mechanism while investigating Orca's long-running "terminal intermittently stops accepting input" issue (stablyai/orca#8104). In the live wedged process, _writeQueue held every lost keystroke with the first task still at offset: 0, and hot-patching write to fs.writeSync restored input instantly — the fd was fine, only the threadpool path was dead.
We're investigating the Electron/libuv threadpool wedge separately. This issue is about how node-pty behaves when it happens: input stays queued indefinitely with no error and no recovery path.
Deterministic repro
The wedge can be simulated exactly by pinning every threadpool worker with a FIFO reader-open (blocks in open() until a writer appears):
constfs=require('fs');constpty=require('node-pty');const{ execFileSync }=require('child_process');execFileSync('mkfifo',['/tmp/stall.fifo']);constp=pty.spawn('/bin/sh',['-c','read line; echo "GOT:$line"'],{});p.onData((d)=>process.stdout.write(d));setTimeout(()=>{for(leti=0;i<6;i++)fs.open('/tmp/stall.fifo','r',()=>{});setTimeout(()=>{p.write('hello\n');// never reaches the shellfs.stat('/tmp',()=>console.log('pool alive'));// never prints},300);},300);
Activity