From df2325c19b9af108a46d2abf9fa8ef7894e92952 Mon Sep 17 00:00:00 2001 From: wangshu94 <1044718972@qq.com> Date: Tue, 21 Apr 2026 22:49:22 +0800 Subject: [PATCH] fix(gateway): surface chat.send lifecycle errors to clients chat.send created the abort controller but never registered the run in chatRunState.registry. Lifecycle phase=error events then hit the skipChatErrorFinal = isChatSendRunActive(runId) && !chatLink guard in server-chat.ts and were dropped, so TUI/Web UI hung in 'waiting' on any agent failure (401 auth, billing, timeout). /abort reported 'no active run' because no chat event ever reached the client. Register the chat run right after the abort controller is created so finalizeLifecycleEvent can resolve chatLink and emit chat.state='error' as expected. Cleanup is already handled by finalizeLifecycleEvent's registry.shift and abortChatRun's removeChatRun. Also patch createChatContext() in chat.directive-tags.test.ts: the harness was missing addChatRun even though SharedChatContext requires it, which broke 37 chat.send tests after the fix. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/gateway/server-methods/chat.directive-tags.test.ts | 2 ++ src/gateway/server-methods/chat.ts | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/gateway/server-methods/chat.directive-tags.test.ts b/src/gateway/server-methods/chat.directive-tags.test.ts index 5d4412318c7..b6af0120a4d 100644 --- a/src/gateway/server-methods/chat.directive-tags.test.ts +++ b/src/gateway/server-methods/chat.directive-tags.test.ts @@ -307,6 +307,7 @@ function createChatContext(): Pick< | "chatDeltaSentAt" | "chatDeltaLastBroadcastLen" | "chatAbortedRuns" + | "addChatRun" | "removeChatRun" | "dedupe" | "loadGatewayModelCatalog" @@ -322,6 +323,7 @@ function createChatContext(): Pick< chatDeltaSentAt: new Map(), chatDeltaLastBroadcastLen: new Map(), chatAbortedRuns: new Map(), + addChatRun: vi.fn(), removeChatRun: vi.fn(), dedupe: new Map(), loadGatewayModelCatalog: async () => diff --git a/src/gateway/server-methods/chat.ts b/src/gateway/server-methods/chat.ts index cea6b4707f4..5a8c7c608ee 100644 --- a/src/gateway/server-methods/chat.ts +++ b/src/gateway/server-methods/chat.ts @@ -2248,6 +2248,10 @@ export const chatHandlers: GatewayRequestHandlers = { ownerConnId: normalizeOptionalText(client?.connId), ownerDeviceId: normalizeOptionalText(client?.connect?.device?.id), }); + context.addChatRun(clientRunId, { + sessionKey, + clientRunId, + }); const ackPayload = { runId: clientRunId, status: "started" as const, @@ -2740,8 +2744,11 @@ export const chatHandlers: GatewayRequestHandlers = { }) .finally(() => { context.chatAbortControllers.delete(clientRunId); + context.removeChatRun(clientRunId, clientRunId, sessionKey); }); } catch (err) { + context.chatAbortControllers.delete(clientRunId); + context.removeChatRun(clientRunId, clientRunId, sessionKey); const error = errorShape(ErrorCodes.UNAVAILABLE, String(err)); const payload = { runId: clientRunId,