Skip to content

Drain live AI streams until the body closes - #204

Merged
Programcode merged 4 commits into
mainfrom
cursor/live-stream-drain-a248
Oct 23, 2026
Merged

Programcode merged 4 commits into
mainfrom
cursor/live-stream-drain-a248

Conversation

@Programcode

Copy link
Copy Markdown
Owner

Summary

Live AI requests could abort the fetch in the gap after the answer was already delivered. readLiveResponse stopped at the result SSE event, and finally always called reader.cancel(). The edge function closes the stream a moment after writing result. 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 own cancel() 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 as result arrives, so the drain does not delay the UI. A user abort still cancels immediately and still rejects with AbortError. Failed result events are returned the same way as before.

Other stream readers

Crossfire, sparring, and the other live features (ask, attack, brainstorm, factoids, generate) all use readLiveResponse, 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 until done. It still calls reader.cancel() in finally after 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 until done or 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.ts readOpenRouterStream stops at [DONE] and always cancels.
  • supabase/functions/ai-stream/index.ts breaks on [DONE] and always cancels the upstream reader in finally.

Tests

src/test/ai/live.test.ts covers:

  • a body that closes after result is not cancelled, and the result is returned before that close
  • a body that stays open through the 2 second cap is cancelled
  • a user abort cancels immediately and rejects with AbortError

Verification

npm run verify exited 0: docs check, lint, typecheck, 449 unit tests, 30 collaboration tests, 406 edge tests, production build, service worker check, and 43 desktop Chromium @smoke tests.

On this VM, global commit signing makes git commit inside backend-status.test.ts stall after about 15 signatures and trip that file's 10 second hook timeout. The verify process turned commit.gpgsign off so those temporary repositories match an unsigned CI git. The command was still npm run verify.

Draft only. Do not merge: merging to main deploys the production frontend.

Open in Web Open in Cursor 

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>
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Next included review available in 8 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 508f243 and e4d9a99.

📒 Files selected for processing: 2
  • src/lib/ai/live.ts
  • src/test/ai/live.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Essentials by visiting https://app.coderabbit.ai/settings/billing.

Comment @coderabbitai help to get the list of available commands.

@Programcode
Programcode marked this pull request as ready for review October 23, 2026 01:53
Copilot AI balanced review requested due to automatic review settings October 23, 2026 01:53
@Programcode
Programcode merged commit 45788cf into main Oct 23, 2026
3 of 12 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants