Feat/fix dashboard timeout error display (#85815)

* fix(gateway): broadcast error to UI when chat.send fails synchronously

* test(gateway): verify broadcastChatError is called on chat.send error

* test(gateway): import GatewayRequestContext from local server-methods barrel

Fixes the chat error-broadcast regression test so it can resolve its
type import. The previous `../types.js` path does not exist in the
gateway tree; the shared types are re-exported from
`src/gateway/server-methods/types.ts`, so the test must use `./types.js`.

Addresses ClawSweeper review on PR #85815.

---------

Co-authored-by: scotthuang <scotthuang@tencent.com>
This commit is contained in:
scotthuang
2026-05-24 09:22:32 +08:00
committed by GitHub
parent 27a3290b53
commit a66898209a
2 changed files with 71 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
import { describe, expect, it, vi } from "vitest";
import { chatHandlers } from "./chat.js";
import type { GatewayRequestContext } from "./types.js";
function createMockContext() {
const broadcast = vi.fn();
const nodeSendToSession = vi.fn();
const chatAbortControllers = new Map();
const agentRunSeq = new Map<string, number>();
const dedupe = new Map();
return {
broadcast,
nodeSendToSession,
chatAbortControllers,
agentRunSeq,
dedupe,
logGateway: { warn: vi.fn(), debug: vi.fn(), error: vi.fn() },
addChatRun: vi.fn(),
removeChatRun: vi.fn(),
};
}
describe("chat.send error broadcast", () => {
it("should broadcast error when addChatRun throws", async () => {
const ctx = createMockContext();
const respond = vi.fn();
// Make addChatRun throw synchronously (inside the try block at line 2470)
ctx.addChatRun.mockImplementation(() => {
throw Object.assign(new Error("LLM timeout"), { code: "TIMEOUT" });
});
await chatHandlers["chat.send"]({
params: {
sessionKey: "main",
message: "hello",
idempotencyKey: "test-run-1",
},
respond: respond as never,
context: ctx as unknown as GatewayRequestContext,
req: {} as never,
client: null as never,
isWebchatConnect: () => false,
});
// Verify respond was called with error
expect(respond).toHaveBeenCalledWith(
false,
expect.objectContaining({ runId: "test-run-1", status: "error" }),
expect.any(Object),
expect.any(Object),
);
// Verify broadcastChatError was called (via context.broadcast)
expect(ctx.broadcast).toHaveBeenCalledWith(
"chat",
expect.objectContaining({
runId: "test-run-1",
state: "error",
errorMessage: expect.stringContaining("LLM timeout"),
}),
);
});
});

View File

@@ -3178,6 +3178,12 @@ export const chatHandlers: GatewayRequestHandlers = {
runId: clientRunId,
error: formatForLog(err),
});
broadcastChatError({
context,
runId: clientRunId,
sessionKey,
errorMessage: String(err),
});
}
},
"chat.inject": async ({ params, respond, context }) => {