Skip to content

fix(client): exclude aborted subrequests from batch messages - #2176

Merged
dinwwwh merged 2 commits into
mainfrom
claude/dazzling-gates-jdyqmh
Oct 4, 2026
Merged

dinwwwh merged 2 commits into
mainfrom
claude/dazzling-gates-jdyqmh

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Oct 4, 2026 •

Copy link
Copy Markdown
Member

Fixes #2172

Summary

Fixes batch link plugin to exclude subrequests that are aborted before the batch is sent to the transport. This prevents the server from counting cancelled requests against its batch size limit and avoids executing calls the client has already rejected.

Changes

  • Filter pending messages before sending: Only include messages for subrequests that are still open (not aborted) when the batch is handed to the transport
  • Apply to both GET and POST: The filtering works for both HTTP methods — GET encodes messages in the URL query parameter, POST in the request body
  • Preserve batch integrity after send: Added a new test case to ensure that subrequests aborted after the batch is sent don't affect the already-sent batch messages

Implementation Details

  • Introduced outgoingMessages variable that filters pendingMessages to only include those with IDs in openRequestIds
  • This filtering happens right before the batch request is constructed, ensuring we only send messages for active subrequests
  • The openRequestIds Set is already maintained by the plugin to track which subrequests are still pending, so this reuses existing tracking logic

https://claude.ai/code/session_01KbSdQ6v2aYW1fT6FmbkS7g

claude added 2 commits October 4, 2026 07:32
…batch

A batched call aborted after its request message was posted, but before
the batch was sent, left both its request and a cancel message in the
batch. The server counts every message against its maxSize, so a full
batch with one cancelled call was rejected with 413 and every other call
in it failed. Below the limit, the server still ran the cancelled call.

The batch request body was also the live message array, so a cancel
posted after the batch was handed to the transport, but before the
transport read the body (e.g. while an async `origin` resolved), could
still end up in the request and trigger the same 413.

Send only the messages of calls that are still open, as a copy taken
when the batch is sent.

Fixes #2172

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbSdQ6v2aYW1fT6FmbkS7g
Drop comments that restate what the batch tests already show, keep only
the reason the server-side size limit matters, and read the sent batch
messages from the transport mock instead of a captured variable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbSdQ6v2aYW1fT6FmbkS7g
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
orpc 0e1fc5d Commit Preview URL

Branch Preview URL
Oct 04 2026, 07:45 AM

@codecov

codecov Bot commented Oct 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pullfrog pullfrog Bot 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.

✅ No new issues found.

Reviewed changes

Reviewed the full diff (2 files, 2 commits) and traced the abort lifecycle through ClientPeer (@standard-server/peer) and BatchHandlerPlugin.

  • Filtered batch payload — executeBatch now snapshots outgoingMessages = pendingMessages.filter(m => openRequestIds.has(m.id)) after isBatchSent = true, so a subrequest aborted (and its cancel message) before send drops out of both the GET data query param and the POST/QUERY body.
  • Boundary is isBatchSent — cancels arriving later are still tracked via cancelledRunningRequestIds and only abort the whole transport request when every open subrequest is cancelled, so an in-flight batch is never rewritten.
  • Tests — the pre-send abort case is now parameterized over POST/GET and asserts the batch carries only the open request; a new test aborts inside transport.send and pins that the sent messages stay unchanged.

The openRequestIds-keyed filter is sound: every open id is guaranteed to have a matching request message (the id is added before markRequestSent, removed only by a cancel), and cancels for open requests cannot exist, so filtering is equivalent to keeping exactly the still-open request messages in order. pendingMessages is no longer referenced after the snapshot, so later pushes cannot mutate the payload.

Verified locally: 59/59 batch.test.ts tests pass, pnpm --filter @orpc/client type:check passes, eslint clean.

The pre-send abort test genuinely fails against the old payload ([['request','/b']] vs. ['request','cancel','request']), so the coverage is real. QUERY is exercised via the shared else body branch even though the parameterized test only names POST/GET.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@pkg-pr-new

pkg-pr-new Bot commented Oct 4, 2026

Copy link
Copy Markdown
More templates

@orpc/ai-sdk

npm i https://pkg.pr.new/@orpc/ai-sdk@2176

@orpc/arktype

npm i https://pkg.pr.new/@orpc/arktype@2176

@orpc/bun

npm i https://pkg.pr.new/@orpc/bun@2176

@orpc/client

npm i https://pkg.pr.new/@orpc/client@2176

@orpc/cloudflare

npm i https://pkg.pr.new/@orpc/cloudflare@2176

@orpc/contract

npm i https://pkg.pr.new/@orpc/contract@2176

@orpc/experimental-effect

npm i https://pkg.pr.new/@orpc/experimental-effect@2176

@orpc/evlog

npm i https://pkg.pr.new/@orpc/evlog@2176

@orpc/hibernation

npm i https://pkg.pr.new/@orpc/hibernation@2176

@orpc/json-schema

npm i https://pkg.pr.new/@orpc/json-schema@2176

@orpc/experimental-lock

npm i https://pkg.pr.new/@orpc/experimental-lock@2176

@orpc/experimental-msw

npm i https://pkg.pr.new/@orpc/experimental-msw@2176

@orpc/nest

npm i https://pkg.pr.new/@orpc/nest@2176

@orpc/next

npm i https://pkg.pr.new/@orpc/next@2176

@orpc/node

npm i https://pkg.pr.new/@orpc/node@2176

@orpc/openapi

npm i https://pkg.pr.new/@orpc/openapi@2176

@orpc/opentelemetry

npm i https://pkg.pr.new/@orpc/opentelemetry@2176

@orpc/pinia-colada

npm i https://pkg.pr.new/@orpc/pinia-colada@2176

@orpc/pino

npm i https://pkg.pr.new/@orpc/pino@2176

@orpc/publisher

npm i https://pkg.pr.new/@orpc/publisher@2176

@orpc/ratelimit

npm i https://pkg.pr.new/@orpc/ratelimit@2176

@orpc/server

npm i https://pkg.pr.new/@orpc/server@2176

@orpc/shared

npm i https://pkg.pr.new/@orpc/shared@2176

@orpc/swr

npm i https://pkg.pr.new/@orpc/swr@2176

@orpc/tanstack-query

npm i https://pkg.pr.new/@orpc/tanstack-query@2176

@orpc/trpc

npm i https://pkg.pr.new/@orpc/trpc@2176

@orpc/valibot

npm i https://pkg.pr.new/@orpc/valibot@2176

@orpc/zod

npm i https://pkg.pr.new/@orpc/zod@2176

commit: 0e1fc5d

@codspeed

codspeed Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will improve performance by 12.02%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
✅ 29 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
⚡ serve deeply nested encoded path 1.2 ms 1 ms +12.02%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing claude/dazzling-gates-jdyqmh (0e1fc5d) with main (161cdc0)

Open in CodSpeed

@dinwwwh
dinwwwh merged commit 7103c64 into main Oct 4, 2026
12 checks passed
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.

BatchLinkPlugin: a call cancelled before its batch is sent still sends its request and cancel, so a full batch fails with 413

2 participants