diff --git a/config/patches/node-pty@1.1.0.patch b/config/patches/node-pty@1.1.0.patch index 01308155569..412de53b19d 100644 --- a/config/patches/node-pty@1.1.0.patch +++ b/config/patches/node-pty@1.1.0.patch @@ -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; + } + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 176199c35bf..40407e156c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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 diff --git a/src/main/daemon/node-pty-windows-input-error.win32.test.ts b/src/main/daemon/node-pty-windows-input-error.win32.test.ts index db529b410cc..416c247774d 100644 --- a/src/main/daemon/node-pty-windows-input-error.win32.test.ts +++ b/src/main/daemon/node-pty-windows-input-error.win32.test.ts @@ -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 { @@ -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) })