fix: infinite loop when quitting caused by closed terminal
Massimo Melina committed
May 6, 2026 at 00:58 UTC
a0628016bc9e2905e705cb844b2eed7f4fa045c5
1 file changed
+23
-2
src/consoleLog.ts
+23
-2
@@ -6,6 +6,14 @@ import { argv } from './argv'
6
export const consoleLog: Array<{ ts: Date, k: string, msg: string }> = []
7
const originalConsoleLog = console.log
8
const f = argv.consoleFile ? createWriteStream(argv.consoleFile, { flags: 'a', encoding: 'utf8' }) : null
9
+let terminalOutputBroken = false
10
+for (const stream of [process.stdout, process.stderr])
11
+ stream.on('error', err => {
12
+ if (!isBrokenTerminalOutput(err))
13
+ throw err
14
+ // after the terminal/pipe is gone, further console writes would just re-emit the same process-level error
15
+ terminalOutputBroken = true
16
+ })
17
for (const k of ['log','warn','error','debug'] as const) {
18
const original = console[k]
19
console[k as 'log'] = (...args: any[]) => {
@@ -23,7 +31,14 @@ for (const k of ['log','warn','error','debug'] as const) {
31
if (k !== 'log')
32
args.unshift('!')
33
}
26
- return original(formatTime(ts), ...args) // bundled nodejs doesn't have locales (and apparently uses en-US)
34
+ if (!terminalOutputBroken) {
35
+ try { return original(formatTime(ts), ...args) } // bundled nodejs doesn't have locales (and apparently uses en-US)
36
+ catch (err) {
37
+ if (!isBrokenTerminalOutput(err))
38
+ throw err
39
+ terminalOutputBroken = true
40
+ }
41
+ }
42
}
43
Object.assign(console[k], { original })
44
}
@@ -56,6 +71,12 @@ function safeJoin(a: unknown[]): string {
71
}
72
}
73
74
+function isBrokenTerminalOutput(err: unknown) {
75
+ const code = (err as NodeJS.ErrnoException)?.code
76
+ return code === 'EPIPE' || code === 'EIO'
77
+ || code === 'ERR_STREAM_DESTROYED' || code === 'ERR_STREAM_WRITE_AFTER_END'
78
+}
79
+
80
export function consoleHint(msg: string) {
81
console.log("HINT: "+ msg)
61
-}
\ No newline at end of file
82
+}