Repository navigation
Drain live AI streams until the body closes - #204
Merged
Merged
Conversation
readLiveResponse stopped at the result event and always cancelled the reader. The edge function closes the body a moment later, so that cancel could abort the fetch and drop the response. Keep reading until the stream ends, or for two seconds, and cancel only if it is still open. The answer is returned as soon as result arrives. Co-authored-by: Gáborjáni-Szabó Ádám <Programcode@users.noreply.github.com>
|
Warning Review limit reachedEnable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Next included review available in 8 minutes. View limit details
Note 🎁 Summarized by CodeRabbit Free
Comment |
This was referenced Oct 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Live AI requests could abort the fetch in the gap after the answer was already delivered.
readLiveResponsestopped at theresultSSE event, andfinallyalways calledreader.cancel(). The edge function closes the stream a moment after writingresult. When the cancel won that race, Chrome aborted the fetch (net::ERR_ABORTED) and dropped the body. Playwright then reported "No data found for resource", and the function's owncancel()ran after the answer had been delivered.After
result, the reader keeps going until the body ends, bounded by the caller's abort signal and a 2 second cap.reader.cancel()runs only when the body has not ended: a read error, the user aborting, or the cap. The answer is returned as soon asresultarrives, so the drain does not delay the UI. A user abort still cancels immediately and still rejects withAbortError. Failedresultevents are returned the same way as before.Other stream readers
Crossfire, sparring, and the other live features (
ask,attack,brainstorm,factoids,generate) all usereadLiveResponse, so this one change covers them.These are not the same pattern and are unchanged:
src/lib/ai/streaming.ts(streamAskChat, the ai-stream client) already reads untildone. It still callsreader.cancel()infinallyafter a clean end. Once the body is closed that cancel is a no-op, so it does not have this race.src/lib/ai/request.ts(extractSupabaseErrorPayload) reads an error JSON body untildoneor a byte cap, and cancels only when the cap is exceeded.These stop early and then cancel, but they are the edge function reading the provider, not the browser reading our response:
supabase/functions/ai/provider.tsreadOpenRouterStreamstops at[DONE]and always cancels.supabase/functions/ai-stream/index.tsbreaks on[DONE]and always cancels the upstream reader infinally.Tests
src/test/ai/live.test.tscovers:resultis not cancelled, and the result is returned before that closeAbortErrorVerification
npm run verifyexited 0: docs check, lint, typecheck, 449 unit tests, 30 collaboration tests, 406 edge tests, production build, service worker check, and 43 desktop Chromium@smoketests.On this VM, global commit signing makes
git commitinsidebackend-status.test.tsstall after about 15 signatures and trip that file's 10 second hook timeout. The verify process turnedcommit.gpgsignoff so those temporary repositories match an unsigned CI git. The command was stillnpm run verify.Draft only. Do not merge: merging to
maindeploys the production frontend.