From e451901ea18486e9044054e52947f055a068d48e Mon Sep 17 00:00:00 2001 From: Ani Tunturi Date: Tue, 17 Mar 2026 13:43:09 -0400 Subject: [PATCH] fix(main): scope Olm error handler removal instead of clobbering globals Surgically removes only the Olm-registered rethrow handler by inspecting listener source, rather than nuking all process error handlers. Adds a safety-net logger only when no other handlers remain. Addresses reviewer feedback: global process.removeAllListeners was removing handlers registered by other parts of the app/runtime. --- src/main.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main.ts b/src/main.ts index 2f538af..545451f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -590,12 +590,27 @@ async function main() { await gateway.start(); // Olm WASM (matrix-js-sdk) registers process.on("uncaughtException", (e) => { throw e }) - // during Olm.init(). Without this fix, any uncaught async exception crashes the bot. - // Must run AFTER gateway.start() since that's when the Matrix adapter initialises Olm. - process.removeAllListeners('uncaughtException'); - process.removeAllListeners('unhandledRejection'); - process.on('uncaughtException', (err) => { log.error('Uncaught exception (suppressed):', err); }); - process.on('unhandledRejection', (reason) => { log.error('Unhandled rejection (suppressed):', reason); }); + // during Olm.init(). That rethrow handler turns any uncaught async error into a crash. + // Surgically remove only the Olm-registered rethrow handlers, preserving any others. + for (const event of ['uncaughtException', 'unhandledRejection'] as const) { + const listeners = process.listeners(event); + for (const listener of listeners) { + // Olm's handler is a one-liner that rethrows: (e) => { throw e } + // Detect by source — short function body that just throws its argument. + const src = listener.toString(); + if (src.includes('throw') && src.length < 50) { + process.removeListener(event, listener as (...args: unknown[]) => void); + } + } + } + // Safety net: log unhandled errors instead of crashing. + // Only adds if no other handlers remain (avoids clobbering app-registered handlers). + if (process.listenerCount('uncaughtException') === 0) { + process.on('uncaughtException', (err) => { log.error('Uncaught exception (suppressed):', err); }); + } + if (process.listenerCount('unhandledRejection') === 0) { + process.on('unhandledRejection', (reason) => { log.error('Unhandled rejection (suppressed):', reason); }); + } // Start API server - uses gateway for delivery const apiPort = parseInt(process.env.PORT || '8080', 10);