fix(telegram): keep webhook monitor alive until abort

Co-authored-by: Evgeny Zislis <7056+kesor@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-02-22 17:45:23 +01:00
parent e58054b85c
commit 1a9b5840d2
3 changed files with 29 additions and 0 deletions

View File

@@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Telegram/Webhook: keep webhook monitors alive until gateway abort signals fire, preventing false channel exits and immediate webhook auto-restart loops.
- Signal/RPC: guard malformed Signal RPC JSON responses with a clear status-scoped error and add regression coverage for invalid JSON responses. (#22995) Thanks @adhitShet.
- Gateway/Subagents: guard gateway and subagent session-key/message trim paths against undefined inputs to prevent early `Cannot read properties of undefined (reading 'trim')` crashes during subagent spawn and wait flows.
- Agents/Workspace: guard `resolveUserPath` against undefined/null input to prevent `Cannot read properties of undefined (reading 'trim')` crashes when workspace paths are missing in embedded runner flows.

View File

@@ -286,6 +286,25 @@ describe("monitorTelegramProvider (grammY)", () => {
expect(runSpy).not.toHaveBeenCalled();
});
it("webhook mode waits for abort signal before returning", async () => {
const abort = new AbortController();
const settled = vi.fn();
const monitor = monitorTelegramProvider({
token: "tok",
useWebhook: true,
webhookUrl: "https://example.test/telegram",
webhookSecret: "secret",
abortSignal: abort.signal,
}).then(settled);
await Promise.resolve();
expect(settled).not.toHaveBeenCalled();
abort.abort();
await monitor;
expect(settled).toHaveBeenCalledTimes(1);
});
it("falls back to configured webhookSecret when not passed explicitly", async () => {
await monitorTelegramProvider({
token: "tok",

View File

@@ -178,6 +178,15 @@ export async function monitorTelegramProvider(opts: MonitorTelegramOpts = {}) {
abortSignal: opts.abortSignal,
publicUrl: opts.webhookUrl,
});
if (opts.abortSignal && !opts.abortSignal.aborted) {
await new Promise<void>((resolve) => {
const onAbort = () => {
opts.abortSignal?.removeEventListener("abort", onAbort);
resolve();
};
opts.abortSignal.addEventListener("abort", onAbort, { once: true });
});
}
return;
}