Windows: ConoutConnection worker thread prevents Node.js from exiting after kill()
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 70/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- node.js, typescript
- Domain
- cli
Research direction
Reproduce the Windows ConPTY hang with the provided Node.js script, then inspect src/windowsConoutConnection.ts, src/worker/conoutSocketWorker.ts, and src/windowsPtyAgent.ts at the referenced lines. Check the worker, sockets, and cleanup timeouts involved after kill(). Done means the process exits after the exit event without requiring process.exit(), while cleanup still completes when other work keeps the event loop alive.
Written by the indexing model from the issue text.
Description
Environment
- OS: Windows (ConPTY path)
- node-pty version: 1.2.0-beta.10
- Node.js: v22.22.0
Description
After calling kill() on a Windows PTY and awaiting its exit event, Node.js cannot exit because active handles remain on the event loop. This forces consumers to call process.exit() as a workaround.
The root cause is that the ConoutConnection worker thread, its internal sockets, and several cleanup timeouts are never unref()'d. There is no .unref() call anywhere in node-pty's source.
Reproduction
const pty = require('node-pty');
const term = pty.spawn('cmd.exe', [], { cols: 80, rows: 24 });
term.onExit(() => {
console.log('exited');
// Node.js should exit here, but it hangs indefinitely
});
setTimeout(() => term.kill(), 500);
Expected: process exits after "exited" is logged.
Actual: process hangs indefinitely.
Root cause
There are three categories of handles keeping the event loop alive after kill():
1. Worker thread (primary)
src/windowsConoutConnection.ts:47 — the Worker is created but never unref()'d:
this._worker = new Worker(join(scriptPath, 'worker/conoutSocketWorker.js'), { workerData });
The worker runs a net.Socket + net.createServer (src/worker/conoutSocketWorker.ts:11-17) that are also never unref()'d. Both the worker thread and its internal sockets keep the event loop alive.
2. Drain timeout
When dispose() is called, it schedules a 1-second timeout before terminating the worker (src/windowsConoutConnection.ts:76):
this._drainTimeout = setTimeout(() => this._destroySocket(), FLUSH_DATA_INTERVAL);
This timeout is not unref()'d, keeping the event loop alive for the duration.
A similar un-unref()'d timeout exists in WindowsPtyAgent._flushDataAndCleanUp() (src/windowsPtyAgent.ts:179):
this._closeTimeout = setTimeout(() => this._cleanUpProcess(), FLUSH_DATA_INTERVAL);
3. I/O sockets
_outSocket and _inSocket in WindowsPtyAgent (src/windowsPtyAgent.ts:78-94) are never unref()'d.
Suggested fix
Call unref() on handles that shouldn't prevent process exit:
// windowsConoutConnection.ts — constructor
this._worker = new Worker(join(scriptPath, 'worker/conoutSocketWorker.js'), { workerData });
this._worker.unref();
// windowsConoutConnection.ts — _drainDataAndClose
this._drainTimeout = setTimeout(() => this._destroySocket(), FLUSH_DATA_INTERVAL);
this._drainTimeout.unref();
// windowsPtyAgent.ts — constructor (sockets)
this._outSocket.unref();
this._inSocket.unref();
// windowsPtyAgent.ts — _flushDataAndCleanUp
this._closeTimeout = setTimeout(() => this._cleanUpProcess(), FLUSH_DATA_INTERVAL);
this._closeTimeout.unref();
This ensures cleanup completes if other work keeps the event loop alive, but doesn't prevent the process from exiting when everything else is done.
- Dominant language
- TypeScript
- Stars
- 2k
- Forks
- 337
- Avg merge
- 21h 58m
- Merged PRs (30d)
- 3
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/node-pty
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
All issues in microsoft/node-pty
Similar issues
-
bug(cli): hapi doctor inline-media prints a fabricated B:\ helper-script path in packaged installs Open
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Crush Open
Difficulty 1/5 Under an hour Newbie friendliness 85/100
catppuccin/catppuccin#3125 ·
-
Add a SECURITY.md Open
Difficulty 1/5 Under an hour Newbie friendliness 90/100
ElementsProject/cln-application#167 · 1 comment · 1 reaction ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Quantco/pnpm-licenses#17 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100