Skip to content

Cap RPC streaming decoder buffers - #6802

Merged
tim-smart merged 5 commits into
mainfrom
agent/codex-engineer/3f1f4717
Jul 31, 2026
Merged

Cap RPC streaming decoder buffers#6802
tim-smart merged 5 commits into
mainfrom
agent/codex-engineer/3f1f4717

Conversation

@tim-smart

@tim-smart tim-smart commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • cap incomplete NDJSON and MessagePack frames with a configurable 16 MiB default
  • preserve the existing ndjson, layerNdjson, and layerMsgPack values while adding makeNdjson, layerNdjsonWith, and layerMsgPackWith for custom limits
  • support maxBufferSize: "unbounded" as an explicit opt-out
  • forward serialization limits through Node, Bun, and Deno cluster layers
  • close socket transports with code 1009 when the decoder limit trips
  • add regression tests and patch changesets

Root cause

The framed decoders retained incomplete NDJSON and MessagePack input in per-connection closure state without any upper bound. Socket transports caught decoder failures as ordinary defects, wrote a response, and continued reading from the same connection.

Impact

Incomplete frames can no longer grow a connection's parser state without bound. Applications can tune or explicitly disable the limit per serializer and through cluster layers, while existing value-based call sites retain their source-compatible API and receive the safe default.

Validation

  • nix develop -c pnpm check
  • nix develop -c pnpm test packages/effect/test/rpc packages/effect/test/unstable/ai/McpServer/McpServer.test.ts --run (45 tests)
  • nix develop -c pnpm test packages/platform-node/test/RpcServer.test.ts packages/platform-node/test/cluster/SocketRunner.test.ts --run (97 tests)
  • targeted dprint and oxlint checks

Closes EFF-258

@github-project-automation github-project-automation Bot moved this to Discussion Ongoing in PR Backlog Jul 31, 2026
@changeset-bot

changeset-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3c002a7

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

This PR includes changesets to release 30 packages
Name Type
effect Patch
@effect/platform-node Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-node-shared Patch
@effect/vitest Patch
@effect/ai-anthropic Patch
@effect/ai-openai-compat Patch
@effect/ai-openai Patch
@effect/ai-openrouter Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/openapi-generator 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

@effect-slopcop effect-slopcop Bot added 4.0 bug Something isn't working labels Jul 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
basic.ts 6.63 KB 6.63 KB 0.00 KB (0.00%)
batching.ts 9.42 KB 9.42 KB 0.00 KB (0.00%)
brand.ts 6.31 KB 6.31 KB 0.00 KB (0.00%)
cache.ts 10.12 KB 10.12 KB 0.00 KB (0.00%)
config.ts 20.04 KB 20.04 KB 0.00 KB (0.00%)
differ.ts 19.93 KB 19.93 KB 0.00 KB (0.00%)
http-client.ts 20.97 KB 20.97 KB 0.00 KB (0.00%)
logger.ts 10.28 KB 10.28 KB 0.00 KB (0.00%)
metric.ts 8.55 KB 8.55 KB 0.00 KB (0.00%)
optic.ts 7.33 KB 7.33 KB 0.00 KB (0.00%)
pubsub.ts 14.26 KB 14.26 KB 0.00 KB (0.00%)
queue.ts 11.09 KB 11.09 KB 0.00 KB (0.00%)
schedule.ts 10.27 KB 10.27 KB 0.00 KB (0.00%)
schema-class.ts 18.86 KB 18.86 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 28.67 KB 28.67 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 24.99 KB 24.99 KB 0.00 KB (0.00%)
schema-string-transformation.ts 12.95 KB 12.95 KB 0.00 KB (0.00%)
schema-string.ts 10.65 KB 10.65 KB 0.00 KB (0.00%)
schema-template-literal.ts 14.85 KB 14.85 KB 0.00 KB (0.00%)
schema-toArbitraryLazy.ts 21.66 KB 21.66 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 24.08 KB 24.08 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 18.91 KB 18.91 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 18.73 KB 18.73 KB 0.00 KB (0.00%)
schema-toFormatter.ts 18.59 KB 18.59 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 22.33 KB 22.33 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 19.25 KB 19.25 KB 0.00 KB (0.00%)
schema.ts 18.12 KB 18.12 KB 0.00 KB (0.00%)
stm.ts 12.05 KB 12.05 KB 0.00 KB (0.00%)
stream.ts 9.37 KB 9.37 KB 0.00 KB (0.00%)

@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

  • Buffer cap for NDJSON and MessagePack streaming decoders: Adds a configurable maxBufferSize (default 16 MiB) to ndjson() and makeMsgPack(), raising MaxBufferSizeExceeded when an incomplete frame exceeds the limit.
  • Forwarding through JSON-RPC framing: ndJsonRpc and layerNdJsonRpc now accept and forward maxBufferSize to the underlying ndjson parser.
  • Graceful socket closure on overflow: The socket protocol catches MaxBufferSizeExceeded and writes a CloseEvent with code 1009 instead of encoding a defect response and continuing to read.
  • API migration: ndjson changed from a static RpcSerialization["Service"] value to a factory function accepting StreamingOptions; all internal callers (McpServer.ts, layerNdjson, ndJsonRpc) updated accordingly.
  • Tests: Unit-level tests for ndjson, ndJsonRpc, and makeMsgPack buffer caps, plus a socket-level integration test verifying the close-on-overflow path.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏

Comment thread packages/effect/src/unstable/rpc/RpcServer.ts Outdated

@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

  • Switched instanceof guard to Predicate.isTagged: The socket protocol's MaxBufferSizeExceeded catch in RpcServer.ts now uses Predicate.isTagged(cause, "MaxBufferSizeExceeded") instead of instanceof, and passes String(cause) rather than cause.message to the CloseEvent — both following reviewer feedback and the preferred pattern for tagged-error discrimination in the codebase.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏

Comment thread packages/effect/src/unstable/rpc/RpcSerialization.ts Outdated

@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

  • Exposed StreamingOptions through layerNdjson, layerMsgPack, ndJsonRpc, and layerNdJsonRpc: Converted layerNdjson and layerMsgPack from static Layer constants to factory functions so callers can opt into maxBufferSize at layer construction time. layerMsgPack() with no arguments preserves the existing msgPack default. Added maxBufferSize forwarding through ndJsonRpc and layerNdJsonRpc.
  • Updated all 14 call sites in core and platform packages: Mechanical layerNdjsonlayerNdjson() and layerMsgPacklayerMsgPack() across Bun, Deno, Node cluster modules and their tests.
  • Added layer-forwarding tests: New layerNdjson, layerMsgPack, and ndJsonRpc tests verify that maxBufferSize reaches the underlying decoder when provided through the layer or framing functions.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏

Copy link
Copy Markdown
Contributor Author

Aligned the v4 API with merged #6803 in 9cdd882: restored ndjson, layerNdjson, and layerMsgPack as values; added the matching makeNdjson, layerNdjsonWith, and layerMsgPackWith APIs; added the "unbounded" sentinel and shared limit helper; and forwarded serializationMaxBufferSize through all Node/Bun cluster layers plus the two v4-only Deno counterparts. Added the platform changeset and unbounded/layer-forwarding coverage.

Validation is clean: 45 Effect RPC/MCP tests, 97 Node RPC/cluster tests, targeted formatting/lint, and the full TypeScript check.

@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

The new commit aligns the API shape with the naming conventions used elsewhere in the codebase, so ndjson and layerNdjson remain backward-compatible while the configurable variants sit behind make* and *With names.

  • Restored ndjson, layerNdjson, and layerMsgPack as constants: The previous commit converted these to factory functions, which is a compile-time break. makeNdjson, layerNdjsonWith, and layerMsgPackWith are the new factory entry points; the original constants are back to being plain values calling the defaults.
  • Added "unbounded" as a valid maxBufferSize: A sentinel string that explicitly disables the buffer limit. The isBufferSizeExceeded predicate uses a type guard (maxBufferSize is number) to narrow correctly when the limit applies.
  • Forwarded serializationMaxBufferSize through six cluster layers: Each platform's HTTP and Socket cluster layer now accepts an optional serializationMaxBufferSize option and passes it to the appropriate *With layer factory.
  • Added unbounded-regression tests: New test cases for both NDJSON and MessagePack verify that "unbounded" buffers large incomplete frames without erroring.
  • Added cluster changeset: Patch-level changeset for @effect/platform-node, @effect/platform-bun, and @effect/platform-deno documenting the new serializationMaxBufferSize option.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏

@tim-smart
tim-smart enabled auto-merge (squash) July 31, 2026 01:55
@tim-smart
tim-smart disabled auto-merge July 31, 2026 02:22
@tim-smart
tim-smart merged commit f1bc827 into main Jul 31, 2026
17 checks passed
@tim-smart
tim-smart deleted the agent/codex-engineer/3f1f4717 branch July 31, 2026 02:22
@github-project-automation github-project-automation Bot moved this from Discussion Ongoing to Done in PR Backlog Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0 bug Something isn't working

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant