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.
This commit is contained in:
Ani Tunturi
2026-03-17 13:43:09 -04:00
committed by Ani
parent 7a7393b8c1
commit e451901ea1

View File

@@ -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);