From a87abcee8df735492010c19ffe810ccd64be6979 Mon Sep 17 00:00:00 2001 From: Michael Garcia Date: Sun, 7 Dec 2025 08:02:40 -0500 Subject: [PATCH 1/2] fix: Add timeout to plugin stop requests, correctly propagate termination signals during graceful shutdown --- app.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 187f7c25..3c6f05da 100644 --- a/app.js +++ b/app.js @@ -127,7 +127,16 @@ const unloadPlugin = async (plugin) => { const plg = getPlugin(plugin); if (plg) { logger.info(`unloading plugin ${plugin.PLUGIN_NAME}`); - res = await send(plg, { action: 'stop' }); + // res = await send(plg, { action: 'stop' }); + // Add timeout to prevent hanging if plugin doesn't respond + const timeout = new Promise((resolve) => setTimeout(() => { + logger.error(`[${plugin.PLUGIN_NAME}] stop request timed out`); + resolve(null); + }, 2000)); + + const sendPromise = send(plg, { action: 'stop' }); + res = await Promise.race([sendPromise, timeout]); + plg.cp.kill('SIGINT'); } return res; @@ -162,8 +171,18 @@ const saveConfig = (lastBlockParsed) => { const stopApp = async (signal = 0) => { const lastBlockParsed = await stop(); saveConfig(lastBlockParsed); - // calling process.exit() won't inform parent process of signal - process.kill(process.pid, signal); + + if (signal && signal !== 0) { + // We received a signal (SIGINT/SIGTERM). + // To let the parent know we died of this signal, we need to remove our listeners + // and re-send the signal to ourselves. This triggers the default handler (termination). + process.removeAllListeners('SIGINT'); + process.removeAllListeners('SIGTERM'); + process.kill(process.pid, signal); + } else { + // Normal exit or no specific signal passed + process.exit(0); + } }; // graceful app closing From 9355620e84deeb2fd974a70730392dcfae08aab1 Mon Sep 17 00:00:00 2001 From: Michael Garcia Date: Sun, 4 Jan 2026 07:39:56 -0500 Subject: [PATCH 2/2] fix: Increase plugin stop request timeout from 2s to 6s. --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 3c6f05da..b952d91d 100644 --- a/app.js +++ b/app.js @@ -132,7 +132,7 @@ const unloadPlugin = async (plugin) => { const timeout = new Promise((resolve) => setTimeout(() => { logger.error(`[${plugin.PLUGIN_NAME}] stop request timed out`); resolve(null); - }, 2000)); + }, 6000)); const sendPromise = send(plg, { action: 'stop' }); res = await Promise.race([sendPromise, timeout]);