QA chat SSE: no cross-chunk buffer silently drops the metadata frame (sources + confidence)
Maintainers usually reply within 4 days
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 52/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- typescript
- Domain
- frontend
Research direction
Start in apps/web/src/hooks/use-qa-chat.ts, especially the stream-reading logic around lines 61, 68, 99-127, and 152. Add mocked-stream regression coverage for a frame split mid-JSON, CRLF [DONE], concurrent sends, and abort behavior. Done means metadata is retained, termination and parse failures are handled visibly, and concurrent or aborted sends do not corrupt conversation state.
Written by the indexing model from the issue text.
Description
Origin: deferred bucket (d) from the Tier-3 cr-loop review of PR #167 (merged as 55744ca). Flagged independently by four reviewers. Separate subject from that PR, which only swapped this file's fetch for apiFetch.
apps/web/src/hooks/use-qa-chat.ts consumes the /api/qa SSE stream. Four defects, in severity order.
1. No cross-chunk buffer — frames are silently dropped
:99 does chunk.split('\n') per read, with no carry buffer for a partial frame. A ReadableStream read boundary can fall anywhere, so any SSE event split across two reads is parsed as two invalid halves. Both halves land in the empty catch at :127 and vanish.
The event most likely to be split is the largest one — the metadata frame carrying sources and confidence. So the visible symptom is an answer that renders fine but loses its citations, intermittently, with nothing logged. That is the worst possible shape for a bug: it looks like the model just didn't cite anything.
Fix: keep a buffer across reads, split on \n\n, and retain the trailing partial for the next iteration.
2. [DONE] never matches under CRLF
:100-109 compares against [DONE] exactly. With CRLF line endings the token arrives as [DONE]\r, falls through to JSON.parse, and throws — again into the empty catch. Normalise line endings before comparing.
3. The empty catch is what makes 1 and 2 invisible
:127 swallows every parse failure with no logging. Both defects above would be obvious in a single session with any logging at all. Related: #197 — nothing in apps/web uses the structured logger that exists.
4. Concurrent sends corrupt shared state
:68 assigns a new AbortController over the in-flight one without aborting it, so a second send leaves the first stream running and both write the same state. There is also no abort on unmount. Separately, :61 reads conversationHistory from a stale closure, so rapid sends replay incomplete history to the model — and :152 strands the user's message on abort while the error placeholder gets replayed as assistant history.
Test gaps
No coverage for a frame split across chunk boundaries, no CRLF case, no concurrent-send case. A regression test for #1 needs a mocked stream that deliberately splits a frame mid-JSON — worth writing first, since it fails today.
- Dominant language
- TypeScript
- Stars
- 10
- Forks
- 4
- Avg merge
- 4d 2h
- Merged PRs (30d)
- 7
Getting set up
- Ships a Dockerfile or Docker Compose file
- No pull request template
- No contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from CopilotKit/outpost
-
area: docs area: security roadmap: now
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
CopilotKit/outpost#277 ·
Maintainers usually reply within 4 days
-
area: infrastructure roadmap roadmap: later
Difficulty 1/5 Under an hour Newbie friendliness 74/100
CopilotKit/outpost#179 ·
Maintainers usually reply within 4 days
-
area: ai roadmap roadmap: now
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
CopilotKit/outpost#145 ·
Maintainers usually reply within 4 days
-
area: integrations priority: low roadmap roadmap: later
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
CopilotKit/outpost#124 · 3 comments ·
Maintainers usually reply within 4 days
-
area: integrations priority: low roadmap roadmap: later
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
CopilotKit/outpost#123 · 2 comments ·
Maintainers usually reply within 4 days
All issues in CopilotKit/outpost
Similar issues
-
Dependencies view: `getParent` loops forever on untitled documents, extension host runs out of memoryPossibly taken A pull request linked to this issue is open or already merged. Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 Under an hour Newbie friendliness 82/100
awslabs/visual-asset-management-system#413 ·
Maintainers usually reply within 1 day
-
bug confirmed perf
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
videojs/video.js#9400 · 1 comment ·
Maintainers usually reply within 1 day
-
bug pending triage scope/agent
Difficulty 1/5 Under an hour Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
good first issue hacktoberfest
Difficulty 2/5 Half a day Newbie friendliness 70/100
HelpCode-ai/anythingmcp#996 ·
Maintainers usually reply within 1 day