diff --git a/frontend/src/components/Chat.test.tsx b/frontend/src/components/Chat.test.tsx
index 665be372..d77a61ec 100644
--- a/frontend/src/components/Chat.test.tsx
+++ b/frontend/src/components/Chat.test.tsx
@@ -1691,3 +1691,40 @@ describe("Slash command handling (Story 374)", () => {
).toBeInTheDocument();
});
});
+
+describe("Bug 450: WebSocket error messages displayed in chat", () => {
+ beforeEach(() => {
+ capturedWsHandlers = null;
+ setupMocks();
+ });
+
+ it("AC1: WebSocket error message is shown in chat as an assistant message", async () => {
+ render();
+
+ await waitFor(() => expect(capturedWsHandlers).not.toBeNull());
+
+ act(() => {
+ capturedWsHandlers?.onError("Something went wrong on the server.");
+ });
+
+ expect(
+ await screen.findByText("Something went wrong on the server."),
+ ).toBeInTheDocument();
+ });
+
+ it("AC2: OAuth login URL in WebSocket error is rendered as a clickable link", async () => {
+ render();
+
+ await waitFor(() => expect(capturedWsHandlers).not.toBeNull());
+
+ act(() => {
+ capturedWsHandlers?.onError(
+ "OAuth login required. Please visit: https://example.com/oauth/login",
+ );
+ });
+
+ const link = await screen.findByRole("link", { name: /https:\/\/example\.com\/oauth\/login/ });
+ expect(link).toBeInTheDocument();
+ expect(link).toHaveAttribute("href", "https://example.com/oauth/login");
+ });
+});
diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx
index bad98dc3..3ac5e812 100644
--- a/frontend/src/components/Chat.tsx
+++ b/frontend/src/components/Chat.tsx
@@ -413,6 +413,14 @@ export function Chat({
console.error("WebSocket error:", message);
setLoading(false);
setActivityStatus(null);
+ const markdownMessage = message.replace(
+ /(https?:\/\/[^\s]+)/g,
+ "[$1]($1)",
+ );
+ setMessages((prev) => [
+ ...prev,
+ { role: "assistant", content: markdownMessage },
+ ]);
if (queuedMessagesRef.current.length > 0) {
const batch = queuedMessagesRef.current.map((item) => item.text);
queuedMessagesRef.current = [];