Skip to content

fix(ai-client): cancel abandoned fetch response streams - #1598

Merged
tombeckenham merged 3 commits into
TanStack:mainfrom
quinnj:fix-response-reader-cleanup
Oct 4, 2026
Merged

tombeckenham merged 3 commits into
TanStack:mainfrom
quinnj:fix-response-reader-cleanup

Conversation

@quinnj

@quinnj quinnj commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Malformed chat data and early exits can leave a fetch response open after the client stops reading. This fix cancels unfinished chat and generation responses without delaying errors or iterator completion.

🎯 Changes

  • Start cancellation before releasing the reader lock, including early iterator returns and the chat SSE [DONE] marker.
  • Preserve the original error and release the lock even if cancellation rejects or stays pending. Normal response completion still delivers every chunk.
  • Add real HTTP regressions, cleanup unit tests, documentation, and a patch changeset for @tanstack/ai-client.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested code changes locally with pnpm run test:pr, or these tests do not apply to this pull request.
  • I fully understand the code in this pull request, including any code generated with AI assistance.
  • Docs: I updated docs/ for this change, or this change is not user-facing.
  • Changeset: I added a changeset (pnpm changeset), or this PR does not change a published package.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Root cause

Issue. A chat parse error can leave the HTTP response open. The same leak occurs on early iterator exit or a generation RUN_ERROR.

Cause. Both readStreamLines implementations only called reader.releaseLock() in finally. Releasing the lock does not cancel the body or tell the server that the reader stopped.

Fix. Each reader starts reader.cancel(), observes any rejection, and releases its lock. Cleanup does not wait for a custom cancellation promise or replace the original error.

Possible alternatives

  • Abort the fetch controller. This needs coordination with every caller and cannot directly control a custom fetcher response. The reader already owns the body.
  • Cancel in each parser or client. This duplicates cleanup and misses direct iterator exits. The existing reader finally covers all exits.
  • Await cancellation. A custom cancellation promise can stay pending forever. The five-case probe below shows why cleanup must release the reader without waiting.

Testing

Commands run (Node 24.11.1, pnpm 11.9.0):

  • corepack pnpm --filter @tanstack/ai-client test:lib --run tests/sse-parser-cleanup.test.ts tests/connection-adapters-cleanup.test.ts tests/generation-client.test.ts tests/connection-adapters-resumable.test.ts: 103 passed.
  • NX_DAEMON=false NX_PARALLEL=2 NX_BASE=origin/main corepack pnpm test:pr: all 149 tasks passed. The declaration scan passed for 1,102 files.
  • corepack pnpm --filter @tanstack/ai-e2e test:e2e: 433 passed, three expected skips, no retries. Both new HTTP regressions passed.
  • corepack pnpm exec oxfmt --check .: passed for 3,999 files. Coverage remains a CI check, as required by the contributing guide.

Earlier E2E runs had intermittent failures outside the reader path. A separate clean-main run also failed in an existing test. One unchanged fal documentation snippet failed once in Kiira, then passed unchanged on main and this branch. The exact trigger remains unconfirmed.

Independent reproduction. Agent-written probes ran outside the PR in separate detached worktrees with the same Node version. Each HTTP probe kept the response open. The same probes failed on clean main and passed on 981c49a. Normal EOF, ignored generation input, and original reader errors remained correct.

The commands used each worktree as the working directory:

node --import tsx "$REPRO_DIR/reproduce.mjs" "$WORKTREE"
node --import tsx "$REPRO_DIR/generation-reproduce.mjs" "$WORKTREE"
node --import tsx "$REPRO_DIR/cancel-settlement-reproduce.mjs" "$WORKTREE"

The transcripts below retain result and assertion lines. Stack traces are omitted.

Clean main: chat (1f380ac)
{"format":"sse","mode":"parse-error","responseClosed":false,"heartbeatWrites":26,"received":0,"error":"SyntaxError"}
{"format":"sse","mode":"early-return","responseClosed":false,"heartbeatWrites":26,"received":1,"error":null}
{"format":"sse","mode":"consumer-error","responseClosed":false,"heartbeatWrites":26,"received":1,"error":"Error"}
{"format":"sse","mode":"eof","responseClosed":true,"heartbeatWrites":0,"received":2,"error":null}
{"format":"sse","mode":"done","responseClosed":false,"heartbeatWrites":27,"received":1,"error":null}
{"format":"ndjson","mode":"parse-error","responseClosed":false,"heartbeatWrites":26,"received":0,"error":"SyntaxError"}
{"format":"ndjson","mode":"early-return","responseClosed":false,"heartbeatWrites":26,"received":1,"error":null}
{"format":"ndjson","mode":"consumer-error","responseClosed":false,"heartbeatWrites":26,"received":1,"error":"Error"}
{"format":"ndjson","mode":"eof","responseClosed":true,"heartbeatWrites":0,"received":2,"error":null}
AssertionError [ERR_ASSERTION]: Every completed adapter must close its HTTP response
Node.js v24.11.1
Exit status: 1
PR head: chat (981c49a)
{"format":"sse","mode":"parse-error","responseClosed":true,"heartbeatWrites":1,"received":0,"error":"SyntaxError"}
{"format":"sse","mode":"early-return","responseClosed":true,"heartbeatWrites":0,"received":1,"error":null}
{"format":"sse","mode":"consumer-error","responseClosed":true,"heartbeatWrites":0,"received":1,"error":"Error"}
{"format":"sse","mode":"eof","responseClosed":true,"heartbeatWrites":0,"received":2,"error":null}
{"format":"sse","mode":"done","responseClosed":true,"heartbeatWrites":0,"received":1,"error":null}
{"format":"ndjson","mode":"parse-error","responseClosed":true,"heartbeatWrites":0,"received":0,"error":"SyntaxError"}
{"format":"ndjson","mode":"early-return","responseClosed":true,"heartbeatWrites":0,"received":1,"error":null}
{"format":"ndjson","mode":"consumer-error","responseClosed":true,"heartbeatWrites":0,"received":1,"error":"Error"}
{"format":"ndjson","mode":"eof","responseClosed":true,"heartbeatWrites":0,"received":2,"error":null}
PASS: both adapters close failed or abandoned responses and drain normal EOF
Exit status: 0
Clean main: generation (1f380ac)
{"mode":"early-return","responseClosed":false,"heartbeatWrites":26,"received":1,"error":null}
{"mode":"consumer-error","responseClosed":false,"heartbeatWrites":25,"received":1,"error":"Error"}
{"mode":"eof","responseClosed":true,"heartbeatWrites":0,"received":2,"error":null}
Failed to parse SSE chunk: {invalid json}
[@tanstack/ai-client] Received [DONE] sentinel. This is deprecated — upgrade your @tanstack/ai server package. RUN_FINISHED is the stream terminator.
{"mode":"ignored-lines","responseClosed":true,"heartbeatWrites":0,"received":2,"error":null}
{"mode":"reader-error","errorPreserved":true,"readerUnlocked":true}
AssertionError [ERR_ASSERTION]: Generation responses must close when the consumer stops
Node.js v24.11.1
Exit status: 1
PR head: generation (981c49a)
{"mode":"early-return","responseClosed":true,"heartbeatWrites":1,"received":1,"error":null}
{"mode":"consumer-error","responseClosed":true,"heartbeatWrites":0,"received":1,"error":"Error"}
{"mode":"eof","responseClosed":true,"heartbeatWrites":0,"received":2,"error":null}
Failed to parse SSE chunk: {invalid json}
[@tanstack/ai-client] Received [DONE] sentinel. This is deprecated — upgrade your @tanstack/ai server package. RUN_FINISHED is the stream terminator.
{"mode":"ignored-lines","responseClosed":true,"heartbeatWrites":0,"received":2,"error":null}
{"mode":"reader-error","errorPreserved":true,"readerUnlocked":true}
PASS: generation reader cleanup preserves EOF, errors, and ignored-line semantics
Exit status: 0

Cancellation liveness. A separate probe held each cancellation promise pending. Awaiting cancellation blocked all five operations. This head completed all five and released each reader lock before the cancellation promise settled.

Awaited-cancellation control (005e3b0)
{"format":"sse","mode":"parse-error","settled":false,"cancelCalls":1,"readerLocked":true}
{"format":"sse","mode":"early-return","settled":false,"cancelCalls":1,"readerLocked":true}
{"format":"ndjson","mode":"parse-error","settled":false,"cancelCalls":1,"readerLocked":true}
{"format":"ndjson","mode":"early-return","settled":false,"cancelCalls":1,"readerLocked":true}
{"format":"generation","mode":"run-error","settled":false,"cancelCalls":1,"readerLocked":true}
AssertionError [ERR_ASSERTION]: Cancellation hooks must not block the original error or iterator return
Node.js v24.11.1
Exit status: 1
PR head: pending cancellation (981c49a)
{"format":"sse","mode":"parse-error","settled":true,"error":"SyntaxError","cancelCalls":1,"readerLocked":false}
{"format":"sse","mode":"early-return","settled":true,"error":null,"cancelCalls":1,"readerLocked":false}
{"format":"ndjson","mode":"parse-error","settled":true,"error":"SyntaxError","cancelCalls":1,"readerLocked":false}
{"format":"ndjson","mode":"early-return","settled":true,"error":null,"cancelCalls":1,"readerLocked":false}
{"format":"generation","mode":"run-error","settled":true,"error":"generation failed","isLoading":false,"cancelCalls":1,"readerLocked":false}
PASS: pending custom cancellation preserves terminal errors, iterator return, and lock release
Exit status: 0

Manual test

  1. On main, serve data: {invalid json}\n\n from an HTTP response that stays open.
  2. Read that response through fetchServerSentEvents. The iterator throws, but the server does not receive a response close event.
  3. Repeat on this branch. The same parse error reaches the caller, and the server receives the close event.
  4. For generations, return an open SSE response with RUN_ERROR from a GenerationClient fetcher. The client reports the error and closes the response.

Easy test path. The branch adds 22 cleanup unit tests and two regressions in testing/e2e/tests/error-handling.spec.ts. The browser regression uses native fetch against an open HTTP response. The generation regression exercises the public GenerationClient with native fetch.

Risk / rollback

A custom stream can now observe cancellation when its consumer stops early. Closed streams keep their existing behavior. Failed or pending cancellation cannot hide the original error or delay iterator completion. Revert this PR to restore the previous cleanup behavior.

This change was researched and prepared through AI-driven maintenance, with source review and local checks.

Co-authored by Codex

Summary by CodeRabbit

  • Bug Fixes

    • Streaming responses are closed when parsing fails, reading stops early, an SSE stream reaches [DONE], or generation reports an error. Cancellation failures do not replace the original error or delay error and loading-state updates.
    • Responses that finish normally continue to deliver all their chunks.
  • Documentation

    • Updated guidance to describe response cleanup during streaming.

@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Configuration used: Repository: TanStack/ai/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e20d74cf-62e0-460c-9710-f51dd13407b0
📥 Commits

Reviewing files that changed from the base of the PR and between 981c49a and 7a309da.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: TanStack/ai/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: bc453aa4-ac9d-4c8a-b3c4-2b76c41aad76

📥 Commits

Reviewing files that changed from the base of the PR and between 005e3b0 and 981c49a.

📒 Files selected for processing (7)
  • .changeset/clean-stream-readers.md
  • docs/chat/connection-adapters.md
  • docs/media/generations.md
  • packages/ai-client/src/connection-adapters.ts
  • packages/ai-client/src/sse-parser.ts
  • packages/ai-client/tests/connection-adapters-cleanup.test.ts
  • packages/ai-client/tests/sse-parser-cleanup.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/media/generations.md
  • .changeset/clean-stream-readers.md

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

Stream readers now request cancellation without waiting when iteration exits. They ignore cancellation failures and release reader locks. Tests and documentation cover parsing failures, early exits, SSE [DONE], RUN_ERROR, and response closure.

Changes

Stream response cleanup

Layer / File(s) Summary
Reader cancellation and lock release
packages/ai-client/src/connection-adapters.ts, packages/ai-client/src/sse-parser.ts, packages/ai-client/tests/connection-adapters-cleanup.test.ts, packages/ai-client/tests/sse-parser-cleanup.test.ts, docs/chat/connection-adapters.md
Both stream readers request cancellation without waiting and ignore cancellation failures. Tests cover parsing failures, early exits, reader errors, and normal EOF. The documentation describes these cleanup cases.
Terminal events and response closure
packages/ai-client/tests/connection-adapters-cleanup.test.ts, packages/ai-client/tests/sse-parser-cleanup.test.ts, testing/e2e/tests/error-handling.spec.ts, docs/media/generations.md, .changeset/clean-stream-readers.md
Tests cover cancellation after SSE [DONE] and response closure after RUN_ERROR or a browser-side parsing error. Documentation and the changeset describe these behaviors.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Suggested reviewers: alemtuzlak

Merge Risk: ⚪ Minimal · up to 981c4

No actionable issue is identified that should prevent merging after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 981c4

Reader ownership and original errors are preserved while cleanup stops waiting for custom cancellation hooks. No expanded authority or new security exposure was established, but completing client cleanup does not guarantee that a custom transport has finished closing.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The directly affected resource is the response body owned by each iterator and its associated client completion state. Response content can trigger parsing errors or terminal events, but the inspected cleanup delta does not change request destinations, credentials, tenant identity, or server-side authority.

Trust Boundaries and Controls

  • observed — Each parser obtains a reader through response.body.getReader() and passes that same reader to a cleanup-owning helper. Cancellation is invoked before lock release. Cleanup does not acquire a different response, broader credentials, or additional tool authority.

Resilience and Maintainability Implications

  • observed — Cleanup-hook failures cannot replace an existing stream error or indefinitely delay iterator completion. Stop and dispose abort delivery, while controller-identity checks prevent stale completion from clearing a replacement run's loading state. These controls do not establish that an arbitrary custom fetcher will interrupt a blocked read or that its underlying transport has closed.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 5 files. (3 skipped: 3 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: canceling abandoned fetch response streams in the AI client.
Description check ✅ Passed The description follows the required template, explains the change and root cause, documents testing and release impact, and includes the changeset and documentation updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 5 files. (3 skipped: 3 unsupported.)

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Thanks for the PR, @quinnj! 🙌 @AlemTuzlak will take a look.

Automated pre-review checks

  • ✅ CI passing
  • ✅ No merge conflicts
  • ✅ Changeset present
  • ✅ E2E test changes included

Automated triage — a human review follows.

@github-actions github-actions Bot added the waiting-on: maintainer The ball is in the maintainers’ court label Oct 2, 2026
@tombeckenham
tombeckenham self-requested a review October 3, 2026 06:36
@nx-cloud

nx-cloud Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 7a309da

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 24s View ↗

☁️ Nx Cloud last updated this comment at 2026-10-04 12:09:31 UTC

@pkg-pr-new

pkg-pr-new Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@1598

@tanstack/ai-acp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-acp@1598

@tanstack/ai-angular

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-angular@1598

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@1598

@tanstack/ai-bedrock

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-bedrock@1598

@tanstack/ai-byteplus

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-byteplus@1598

@tanstack/ai-claude-code

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-claude-code@1598

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@1598

@tanstack/ai-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-cloudflare@1598

@tanstack/ai-code-mode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode@1598

@tanstack/ai-code-mode-snippets

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode-snippets@1598

@tanstack/ai-codex

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-codex@1598

@tanstack/ai-cohere

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-cohere@1598

@tanstack/ai-compaction

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-compaction@1598

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@1598

@tanstack/ai-durable-stream

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-durable-stream@1598

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-elevenlabs@1598

@tanstack/ai-event-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-event-client@1598

@tanstack/ai-fal

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-fal@1598

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@1598

@tanstack/ai-grok

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok@1598

@tanstack/ai-grok-build

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok-build@1598

@tanstack/ai-groq

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-groq@1598

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-cloudflare@1598

@tanstack/ai-isolate-daytona

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-daytona@1598

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-node@1598

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-quickjs@1598

@tanstack/ai-isolate-quickjs-bun

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-quickjs-bun@1598

@tanstack/ai-llmgateway

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-llmgateway@1598

@tanstack/ai-lovable

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-lovable@1598

@tanstack/ai-mcp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mcp@1598

@tanstack/ai-memory

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-memory@1598

@tanstack/ai-mistral

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mistral@1598

@tanstack/ai-octane

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-octane@1598

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@1598

@tanstack/ai-ollaya

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollaya@1598

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@1598

@tanstack/ai-opencode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-opencode@1598

@tanstack/ai-openrouter

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openrouter@1598

@tanstack/ai-perplexity

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-perplexity@1598

@tanstack/ai-persistence

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence@1598

@tanstack/ai-preact

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact@1598

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@1598

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@1598

@tanstack/ai-reactor

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-reactor@1598

@tanstack/ai-remix

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-remix@1598

@tanstack/ai-sandbox

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox@1598

@tanstack/ai-sandbox-blaxel

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-blaxel@1598

@tanstack/ai-sandbox-boxd

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-boxd@1598

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-cloudflare@1598

@tanstack/ai-sandbox-daytona

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-daytona@1598

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-docker@1598

@tanstack/ai-sandbox-e2b

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-e2b@1598

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-local-process@1598

@tanstack/ai-sandbox-sprites

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-sprites@1598

@tanstack/ai-sandbox-upstash-box

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-upstash-box@1598

@tanstack/ai-sandbox-vercel

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-vercel@1598

@tanstack/ai-skills

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-skills@1598

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@1598

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@1598

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@1598

@tanstack/ai-typesafe

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-typesafe@1598

@tanstack/ai-utils

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-utils@1598

@tanstack/ai-vercel-gateway

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vercel-gateway@1598

@tanstack/ai-vertex

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vertex@1598

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@1598

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@1598

@tanstack/ai-worldlabs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-worldlabs@1598

@tanstack/openai-base

npm i https://pkg.pr.new/TanStack/ai/@tanstack/openai-base@1598

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/preact-ai-devtools@1598

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@1598

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@1598

@tanstack/svelte-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/svelte-ai-devtools@1598

commit: 7a309da

@tombeckenham tombeckenham left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reproduced the open fetch-body leak on current main. This head closes the response on a chat parse error, an early iterator return, and a generation RUN_ERROR, and it still delivers a normal end.

Approving.

@tombeckenham
tombeckenham enabled auto-merge (squash) October 4, 2026 10:06
@changeset-bot

changeset-bot Bot commented Oct 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7a309da

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@tanstack/ai-client Patch
@tanstack/ai-octane Patch
ag-ui Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added waiting-on: author Waiting for the author to respond or update and removed waiting-on: maintainer The ball is in the maintainers’ court labels Oct 4, 2026
@tombeckenham
tombeckenham merged commit e436250 into TanStack:main Oct 4, 2026
10 of 11 checks passed
@github-actions github-actions Bot mentioned this pull request Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-on: author Waiting for the author to respond or update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants