fix(pty): contain late ConPTY output EPIPE

This commit is contained in:
Neil
2026-08-31 05:43:08 -07:00
parent 3cb8341208
commit b2eb47af72
3 changed files with 42 additions and 5 deletions
+2 -2
View File
@@ -826,7 +826,7 @@ index 3c38f89..e20b3e6 100644
+ // Node can report these duplicate stream errors after teardown; active
+ // sockets still surface every other failure to the caller.
+ _this._close();
+ if (wasClosing && (code === 'ERR_STREAM_PUSH_AFTER_EOF' || code === 'ERR_STREAM_DESTROYED')) {
+ if (wasClosing && (code === 'EPIPE' || code === 'ERR_STREAM_PUSH_AFTER_EOF' || code === 'ERR_STREAM_DESTROYED')) {
+ return;
+ }
+ // EIO, happens when someone closes our child process: the only process
@@ -936,7 +936,7 @@ index 13f6c6d..eda63c8 100644
+ // Node can report these duplicate stream errors after teardown; active
+ // sockets still surface every other failure to the caller.
+ this._close();
+ if (wasClosing && (code === 'ERR_STREAM_PUSH_AFTER_EOF' || code === 'ERR_STREAM_DESTROYED')) {
+ if (wasClosing && (code === 'EPIPE' || code === 'ERR_STREAM_PUSH_AFTER_EOF' || code === 'ERR_STREAM_DESTROYED')) {
+ return;
+ }
+
+3 -3
View File
@@ -115,7 +115,7 @@ patchedDependencies:
'@xterm/addon-webgl@0.20.0-beta.299': 94687e89a0115e6e6aa102837f986debdc029c091527ee5eb4a4e17ceaf9473e
'@xterm/xterm@6.1.0-beta.303': 98756bcedc402bcdb7c6ab7b015d2e59cd18e97b03a2c06a27e95bb3ba429d9d
lint-staged@16.4.0: 7333b3837f80a7fbd045964db6d76ba4fc118e49134bdbabb00585b6b7b60673
node-pty@1.1.0: 03fca170d33846f3701717560abda8da9d993c1489e602164bc68e6c0200ed5f
node-pty@1.1.0: 1d4405bccb8ad7cb0c45061306a11059f02a23b9f60df8d14ef0563f88f84b0b
importers:
@@ -156,7 +156,7 @@ importers:
version: 3.3.1
node-pty:
specifier: ^1.1.0
version: 1.1.0(patch_hash=03fca170d33846f3701717560abda8da9d993c1489e602164bc68e6c0200ed5f)
version: 1.1.0(patch_hash=1d4405bccb8ad7cb0c45061306a11059f02a23b9f60df8d14ef0563f88f84b0b)
posthog-node:
specifier: ^5.33.3
version: 5.33.3
@@ -12194,7 +12194,7 @@ snapshots:
node-int64@0.4.0: {}
node-pty@1.1.0(patch_hash=03fca170d33846f3701717560abda8da9d993c1489e602164bc68e6c0200ed5f):
node-pty@1.1.0(patch_hash=1d4405bccb8ad7cb0c45061306a11059f02a23b9f60df8d14ef0563f88f84b0b):
dependencies:
node-addon-api: 7.1.1
@@ -4,6 +4,7 @@ import { describe, expect, it } from 'vitest'
type WindowsPtyInternals = IPty & {
_agent: { inSocket: Socket }
_socket: Socket
}
function waitForOutput(terminal: IPty, marker: string): Promise<void> {
@@ -78,4 +79,40 @@ describe.skipIf(process.platform !== 'win32')('node-pty Windows input errors', (
process.off('uncaughtException', uncaughtListener)
}
}, 20_000)
it('ignores a late output EPIPE after the PTY has closed', async () => {
const uncaught: unknown[] = []
const uncaughtListener = (error: unknown): void => {
uncaught.push(error)
}
process.on('uncaughtException', uncaughtListener)
let terminal: IPty | undefined
try {
terminal = spawn(process.env.ComSpec ?? 'cmd.exe', ['/d', '/q'], {
cwd: process.cwd(),
env: process.env,
useConptyDll: false
})
terminal.kill()
await waitForExit(terminal)
const output = (terminal as WindowsPtyInternals)._socket
expect(() => {
output.emit(
'error',
Object.assign(new Error('This socket has been ended by the other party'), {
code: 'EPIPE'
})
)
}).not.toThrow()
expect(uncaught).toEqual([])
} finally {
try {
terminal?.kill()
} catch {}
await new Promise((resolve) => setTimeout(resolve, 1_500))
process.off('uncaughtException', uncaughtListener)
}
}, 20_000)
})