diff --git a/extensions/irc/src/active-clients.ts b/extensions/irc/src/active-clients.ts index 47ac90c747f..cd960e201be 100644 --- a/extensions/irc/src/active-clients.ts +++ b/extensions/irc/src/active-clients.ts @@ -22,3 +22,14 @@ export function getActiveClient(accountId: string): IrcClient | undefined { export function removeActiveClient(accountId: string): void { activeClients.delete(accountId); } + +/** + * Only remove the active client if it matches the expected instance. + * Prevents a stopping monitor from deregistering a newer monitor's + * healthy client during reconnect races. + */ +export function removeActiveClientIfMatch(accountId: string, expected: IrcClient): void { + if (activeClients.get(accountId) === expected) { + activeClients.delete(accountId); + } +} diff --git a/extensions/irc/src/monitor.ts b/extensions/irc/src/monitor.ts index 8ba5fd95338..6eb487f586f 100644 --- a/extensions/irc/src/monitor.ts +++ b/extensions/irc/src/monitor.ts @@ -1,6 +1,6 @@ import { resolveLoggerBackedRuntime } from "openclaw/plugin-sdk/extension-shared"; import { resolveIrcAccount } from "./accounts.js"; -import { setActiveClient, removeActiveClient } from "./active-clients.js"; +import { setActiveClient, removeActiveClientIfMatch } from "./active-clients.js"; import { connectIrcClient, type IrcClient } from "./client.js"; import { buildIrcConnectOptions } from "./connect-options.js"; import { handleIrcInbound } from "./inbound.js"; @@ -141,7 +141,9 @@ export async function monitorIrcProvider(opts: IrcMonitorOptions): Promise<{ sto return { stop: () => { - removeActiveClient(account.accountId); + if (client) { + removeActiveClientIfMatch(account.accountId, client); + } client?.quit("shutdown"); client = null; },