fix(FN-920): improve error handling in subtask breakdown SSE stream
- Handle empty error messages in subtask-breakdown session by falling back to 'Unknown error' - Fix backend SSE routes to coerce error data to string and handle missing message - Improve frontend connectSubtaskStream to validate parsed error data before displaying - Add tests for error display in SubtaskBreakdownModal component
This commit is contained in:
@@ -1599,7 +1599,13 @@ export function connectSubtaskStream(
|
|||||||
eventSource.addEventListener("error", (event: Event) => {
|
eventSource.addEventListener("error", (event: Event) => {
|
||||||
try {
|
try {
|
||||||
const messageEvent = event as MessageEvent;
|
const messageEvent = event as MessageEvent;
|
||||||
handlers.onError?.(JSON.parse(messageEvent.data) as string);
|
const parsedData = JSON.parse(messageEvent.data);
|
||||||
|
const errorMessage = typeof parsedData === "string" && parsedData.length > 0 ? parsedData : null;
|
||||||
|
if (errorMessage) {
|
||||||
|
handlers.onError?.(errorMessage);
|
||||||
|
} else {
|
||||||
|
handlers.onError?.("Stream error");
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
handlers.onError?.("Stream error");
|
handlers.onError?.("Stream error");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -373,4 +373,21 @@ describe("SubtaskBreakdownModal", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("error handling", () => {
|
||||||
|
it("displays error message when stream returns error event", async () => {
|
||||||
|
renderModal();
|
||||||
|
await waitFor(() => expect(streamHandlers).toBeDefined());
|
||||||
|
streamHandlers.onError("Something went wrong");
|
||||||
|
expect(await screen.findByText("Something went wrong")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows Stream error fallback when receiving empty error", async () => {
|
||||||
|
renderModal();
|
||||||
|
await waitFor(() => expect(streamHandlers).toBeDefined());
|
||||||
|
// In real flow, api.ts converts empty string to "Stream error" before calling onError
|
||||||
|
streamHandlers.onError("Stream error");
|
||||||
|
expect(await screen.findByText("Stream error")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4950,7 +4950,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (session.status === "error") {
|
if (session.status === "error") {
|
||||||
res.write(`event: error\ndata: ${JSON.stringify(session.error || "Failed to generate subtasks")}\n\n`);
|
res.write(`event: error\ndata: ${JSON.stringify(String(session.error || "Unknown error"))}\n\n`);
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
res.end();
|
res.end();
|
||||||
return;
|
return;
|
||||||
@@ -4969,7 +4969,7 @@ export function createApiRoutes(store: TaskStore, options?: ServerOptions): Rout
|
|||||||
unsubscribe();
|
unsubscribe();
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.write(`event: error\ndata: ${JSON.stringify(err.message || "Stream error")}\n\n`);
|
res.write(`event: error\ndata: ${JSON.stringify(String(err?.message) || "Unknown error")}\n\n`);
|
||||||
res.end();
|
res.end();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ export async function createSubtaskSession(initialDescription: string, _store?:
|
|||||||
const existing = sessions.get(sessionId);
|
const existing = sessions.get(sessionId);
|
||||||
if (!existing) return;
|
if (!existing) return;
|
||||||
existing.status = "error";
|
existing.status = "error";
|
||||||
existing.error = err instanceof Error ? err.message : "Failed to generate subtasks";
|
existing.error = err instanceof Error ? (err.message || "Unknown error") : "Failed to generate subtasks";
|
||||||
existing.updatedAt = new Date();
|
existing.updatedAt = new Date();
|
||||||
persistSubtaskSession(existing, "error", existing.error);
|
persistSubtaskSession(existing, "error", existing.error);
|
||||||
subtaskStreamManager.broadcast(sessionId, { type: "error", data: existing.error });
|
subtaskStreamManager.broadcast(sessionId, { type: "error", data: existing.error });
|
||||||
|
|||||||
Reference in New Issue
Block a user