From cf79735a65ecdca5f5045f1a7dc4a524d321a355 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 17 Jun 2026 17:30:26 +0200 Subject: [PATCH] fix(googlechat): clear status after startup failures --- extensions/googlechat/src/gateway.ts | 57 +++++++++++++++---------- extensions/googlechat/src/setup.test.ts | 18 ++++++++ 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/extensions/googlechat/src/gateway.ts b/extensions/googlechat/src/gateway.ts index 41fbe49fc73f..05512435366c 100644 --- a/extensions/googlechat/src/gateway.ts +++ b/extensions/googlechat/src/gateway.ts @@ -44,6 +44,17 @@ export async function startGoogleChatGatewayAccount(ctx: { audienceType: account.config.audienceType, audience: account.config.audience, }); + let stopped = false; + const markStopped = () => { + if (stopped) { + return; + } + stopped = true; + statusSink({ + running: false, + lastStopAt: Date.now(), + }); + }; if ( isGoogleChatNativeApprovalClientEnabled({ cfg: ctx.cfg, @@ -59,26 +70,28 @@ export async function startGoogleChatGatewayAccount(ctx: { abortSignal: ctx.abortSignal, }); } - await runPassiveAccountLifecycle({ - abortSignal: ctx.abortSignal, - start: async () => - await startGoogleChatMonitor({ - account, - config: ctx.cfg, - runtime: ctx.runtime, - abortSignal: ctx.abortSignal, - webhookPath: account.config.webhookPath, - webhookUrl: account.config.webhookUrl, - statusSink, - }), - stop: async (unregister) => { - unregister?.(); - }, - onStop: async () => { - statusSink({ - running: false, - lastStopAt: Date.now(), - }); - }, - }); + try { + await runPassiveAccountLifecycle({ + abortSignal: ctx.abortSignal, + start: async () => + await startGoogleChatMonitor({ + account, + config: ctx.cfg, + runtime: ctx.runtime, + abortSignal: ctx.abortSignal, + webhookPath: account.config.webhookPath, + webhookUrl: account.config.webhookUrl, + statusSink, + }), + stop: async (unregister) => { + unregister?.(); + }, + onStop: async () => { + markStopped(); + }, + }); + } catch (error) { + markStopped(); + throw error; + } } diff --git a/extensions/googlechat/src/setup.test.ts b/extensions/googlechat/src/setup.test.ts index 803674c9e8b6..ab06d28f6a4a 100644 --- a/extensions/googlechat/src/setup.test.ts +++ b/extensions/googlechat/src/setup.test.ts @@ -1,5 +1,6 @@ // Googlechat tests cover setup plugin behavior. import { + createStartAccountContext, expectLifecyclePatch, expectPendingUntilAbort, startAccountAndTrackLifecycle, @@ -12,6 +13,7 @@ import { } from "openclaw/plugin-sdk/plugin-test-runtime"; import type { WizardPrompter } from "openclaw/plugin-sdk/plugin-test-runtime"; import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/setup"; +import type { ChannelAccountSnapshot } from "openclaw/plugin-sdk/status-helpers"; import { afterAll, afterEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../runtime-api.js"; import { @@ -383,6 +385,22 @@ describe("googlechat setup", () => { expectLifecyclePatch(patches, { running: true }); expectLifecyclePatch(patches, { running: false }); }); + + it("clears running status when monitor startup fails", async () => { + hoisted.startGoogleChatMonitor.mockRejectedValue(new Error("webhook bind failed")); + const patches: ChannelAccountSnapshot[] = []; + + const task = startGoogleChatGatewayAccount( + createStartAccountContext({ + account: buildAccount(), + statusPatchSink: (next) => patches.push({ ...next }), + }), + ); + + await expect(task).rejects.toThrow("webhook bind failed"); + expectLifecyclePatch(patches, { running: true }); + expectLifecyclePatch(patches, { running: false }); + }); }); describe("resolveGoogleChatAccount", () => {