fix(background-agent): detach error listener before running body to stop re-entrant log loop
When shutdown() itself emitted uncaughtException (e.g. EPIPE while closing a broken pipe), the error listener re-entered itself, re-logged, re-ran cleanup, and threw EPIPE again. The 6 s forced-exit timer could not fire because every re-entry stalled the event loop with fresh synchronous work. Users hit this after v3.17.5 and observed 100+ GB of log lines written to disk within minutes, with one confirmed report of a 157 GB log file filling the filesystem. Detaching the listener with process.off() before running log() + handler() breaks the loop at the first re-emit: the second event has no listener to invoke, and the first invocation's scheduleForcedExit() proceeds normally.
This commit is contained in:
@@ -31,6 +31,12 @@ function registerErrorEvent(
|
||||
handler: (error: unknown) => void | Promise<void>
|
||||
): (error: unknown) => void {
|
||||
const listener = (error: unknown) => {
|
||||
// Detach before running the body so a re-emit from inside log()/handler()
|
||||
// (e.g. EPIPE while closing a broken pipe during shutdown) cannot recurse.
|
||||
// Prior behavior: the listener re-entered itself, re-logged, re-ran cleanup,
|
||||
// and threw EPIPE again — an unbounded loop that filled disks with 100+ GB
|
||||
// of log lines in minutes before the 6 s forced-exit timer could fire.
|
||||
process.off(signal, listener)
|
||||
log(`[background-agent] ${signal} received during shutdown cleanup:`, error)
|
||||
scheduleForcedExit(handler(error), 1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user