Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Windows: ConoutConnection worker thread prevents Node.js from exiting after kill()

Open
#887 4 comments 0 reactions 0 assignees View on GitHub

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/node-pty

All issues in microsoft/node-pty

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.