Skip to content

fix(anthropic): bound streaming 200 success-body SSE reads at 16 MiB (provider path)#96723

Closed
wangmiao0668000666 wants to merge 1 commit into
openclaw:mainfrom
wangmiao0668000666:fix/anthropic-provider-stream-bound
Closed

fix(anthropic): bound streaming 200 success-body SSE reads at 16 MiB (provider path)#96723
wangmiao0668000666 wants to merge 1 commit into
openclaw:mainfrom
wangmiao0668000666:fix/anthropic-provider-stream-bound

Conversation

@wangmiao0668000666

@wangmiao0668000666 wangmiao0668000666 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

src/llm/providers/anthropic.ts (iterateSseMessages, the legacy provider SSE parser) accumulates an unbounded SSE byte stream while walking the Anthropic Messages streaming response. A hostile or malfunctioning Anthropic-compatible endpoint can return a Content-Length-less SSE body that streams forever, exhausting process memory on the legacy code path that runs alongside the transport-stream path (#96701).

This is the streaming-body analogue of the bounded-read sweep Alix-007 finished for non-streaming response.json() / response.text(). Same 16 MiB cap, same helper-driven design, on the legacy provider path that #96701 does not cover.

Changes

Reuses the existing createSseByteGuard helper (already in main) — no new abstraction in this PR.

  • src/llm/providers/anthropic.ts:347-362iterateSseMessages wraps body.getReader() in createSseByteGuard with ANTHROPIC_MESSAGES_SUCCESS_BODY_MAX_BYTES = 16 * 1024 * 1024.
  • src/llm/providers/anthropic.test.ts — 1 inline test (38 LoC) through production iterateSseMessages wrapper with oversized stream + negative control + happy path.

Symmetric counterpart to #96701 (anthropic-transport-stream). Aligned with the bounded-read campaign pattern.

Design Rationale

Why reuse createSseByteGuard instead of a new helper? The provider-path SSE parser follows the same chunk-by-chunk reader.read() → buffer → parse loop structure as anthropic-transport-stream. Using the same guard avoids duplicating the byte-accounting, overflow-check, and cancellation logic. The guard's SseByteGuard interface (read/cancel/totalBytes/overflowed/cancelled) was designed for cross-parser reuse from the start.

Why 16 MiB cap? Matches the existing non-streaming 16 MiB cap from readProviderJsonResponse (src/agents/provider-http-errors.ts) and the sibling anthropic-transport-stream PR (#96701). The Anthropic Messages API produces compact streaming responses — typical SSE events are a few KiB, so the 16 MiB cap would only fire on malfunctioning or hostile endpoints.

Why a separate call site, not a shared constant? Each parser (anthropic transport, proxy, provider) evolves independently — the provider-path cap may later be tuned differently if legacy provider patterns emerge. Named constants at each call site keep the override path explicit while the value stays consistent (all 16 MiB).

Real behavior proof

  • Behavior addressed: unbounded buffering of the Anthropic Messages legacy provider-path SSE read; after the fix the read is capped at 16 MiB and the stream is cancelled on overflow.
  • Real environment tested: a real node:http server bound to 127.0.0.1 returning a Content-Length-less 64 MiB body (4× the cap) chunk-by-chunk, plus a negative-control 1 MiB body, plus a happy-path body. Drives the production iterateSseMessages over a real TCP socket. Node v22.22.0.
  • Exact steps: node --import tsx _proof_anthropic_provider_stream.mts (proof script kept local-only, not committed).
  • Evidence after fix:
    PASS  Anthropic provider-path streaming success body bounded: rejected with "Anthropic Messages success body exceeded 16777216 bytes (received 16834186)"; bytesSent=19923323 (< 67108864); server.aborted=true
    PASS  negative control: small body fully drained; cap did not trigger
    PASS  happy path: small valid Anthropic SSE response drained end-to-end
    
  • Observed result after fix: createSseByteGuard cancelled the upstream ReadableStreamDefaultReader at ~16 MiB. Server-side bytesSent observed at ~19 MiB — bounded, not drained.
  • What was not tested: live Anthropic API call (the proof exercises the same production helper against the same shape of attack the live endpoint could carry); cross-platform Node differences (Node 22 only, matches CI).

Out of scope (separate follow-ups)

Risk checklist

  • User-visible behavior change? No — normal-sized responses unaffected; oversized responses now throw a clear overflow error instead of silently accumulating memory.
  • Config/env/migration change? No.
  • Security/auth/secrets/network/tool-execution change? Yes — OOM/DoS protection for Anthropic Messages legacy provider streaming success body (16 MiB cap + reader cancellation on overflow).
  • Highest-risk area: cancel propagation from createSseByteGuard to underlying reader; verified by inline test assertion.

Diff stats

2 files changed, 59 insertions(+), 2 deletions(-)

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Jun 25, 2026
@clawsweeper

clawsweeper Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 27, 2026, 1:00 PM ET / 17:00 UTC.

Summary
The PR wraps the legacy Anthropic Messages provider SSE parser in src/llm/providers/anthropic.ts with the shared 16 MiB createSseByteGuard and adds focused provider-parser regression coverage.

PR surface: Source +20, Tests +37. Total +57 across 2 files.

Reproducibility: yes. from source: current main's iterateSseMessages reads chunks into buffer without a byte cap. The PR body also includes copied live output from a local chunked HTTP proof for overflow, cancellation, negative control, and happy path, though I did not run that proof in this read-only review.

Review metrics: 1 noteworthy metric.

  • Streaming body cap: 1 parser changed from unbounded to 16 MiB. The hard fail-closed limit is the maintainer-visible compatibility choice that green CI cannot settle.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Risk before merge

  • [P1] Existing Anthropic-compatible endpoints that legitimately stream more than 16 MiB in one success body will now fail with an overflow error and cancel instead of continuing.
  • [P1] The related Anthropic transport and runtime proxy streaming caps are still separate open PRs, so this PR only closes the legacy provider-path surface.

Maintainer options:

  1. Accept the 16 MiB provider cap
    Maintainers can accept the same fixed cap used by the bounded-read campaign and proceed after normal merge-result and CI checks.
  2. Tune the cap before merge
    If Anthropic-compatible provider streams have a known legitimate larger success-body contract, adjust the cap and test expectation on this PR before landing.

Next step before merge

  • [P1] Manual review is appropriate because no narrow automated repair remains; maintainers need to accept or tune the fixed 16 MiB fail-closed behavior.

Security
Cleared: The patch reduces a provider-response DoS/OOM vector and does not add dependencies, scripts, secrets handling, workflow permissions, or package-resolution changes.

Review details

Best possible solution:

Land this provider-path guard if maintainers accept the 16 MiB boundary; otherwise tune the cap and matching regression assertion before merge.

Do we have a high-confidence way to reproduce the issue?

Yes from source: current main's iterateSseMessages reads chunks into buffer without a byte cap. The PR body also includes copied live output from a local chunked HTTP proof for overflow, cancellation, negative control, and happy path, though I did not run that proof in this read-only review.

Is this the best way to solve the issue?

Yes, with a compatibility caveat: reusing the existing core guard is the narrowest maintainable fix for this parser. A new SDK export or config knob would be broader than this bugfix unless maintainers reject or tune the fixed threshold.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 2720ac06b7c8.

Label changes

Label justifications:

  • P2: This is a normal-priority provider hardening fix for a bounded OOM/DoS issue with limited blast radius.
  • merge-risk: 🚨 compatibility: The PR intentionally changes oversized Anthropic-compatible streaming success responses from unbounded reads to a thrown overflow error.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body contains copied live output from a real local chunked HTTP server showing the after-fix cap, reader cancellation, negative control, and happy path.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body contains copied live output from a real local chunked HTTP server showing the after-fix cap, reader cancellation, negative control, and happy path.
Evidence reviewed

PR surface:

Source +20, Tests +37. Total +57 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 21 1 +20
Tests 1 38 1 +37
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 59 2 +57

What I checked:

  • Current main provider parser is unbounded: On current main, iterateSseMessages obtains body.getReader() and reads directly with reader.read() while appending decoded chunks to buffer, with no byte cap before SSE parsing. (src/llm/providers/anthropic.ts:360, 2720ac06b7c8)
  • Runtime entry point reaches this parser: streamAnthropic calls client.messages.create({ ...params, stream: true }, requestOptions).asResponse() and then iterates the raw response through iterateAnthropicEvents, which delegates to iterateSseMessages. (src/llm/providers/anthropic.ts:527, 2720ac06b7c8)
  • PR head applies the shared guard: The PR head creates a 16 MiB createSseByteGuard around the provider SSE reader and replaces the loop read with guard.read(). (src/llm/providers/anthropic.ts:356, 7c3dea35e1b2)
  • Shared guard contract: createSseByteGuard tracks accumulated bytes, cancels the underlying reader on overflow, and throws the caller-provided overflow error. (src/agents/streaming-byte-guard.ts:32, 5880e0afc4dc)
  • Regression coverage in PR head: The new test drives iterateSseMessagesForTest with a 32 MiB synthetic stream, asserts the 16 MiB overflow message, and verifies cancellation receives an Error. (src/llm/providers/anthropic.test.ts:1310, 7c3dea35e1b2)
  • Dependency contract checked: Anthropic SDK 0.100.1 APIPromise.asResponse() returns the raw Response, and messages.create sends streaming requests through the SDK post path with stream: body.stream ?? false, so OpenClaw owns the raw response body iteration after calling .asResponse(). (src/core/api-promise.ts:55, 512605fa9b5c)

Likely related people:

  • vincentkoc: Recent GitHub history shows repeated Anthropic provider and Anthropic transport changes in the same streaming/runtime surface. (role: recent area contributor; confidence: high; commits: 9e6332338855, 066700bdd0e4, 536c8a840bda; files: src/llm/providers/anthropic.ts, src/llm/providers/anthropic.test.ts, src/agents/anthropic-transport-stream.ts)
  • wangmiao0668000666: Authored the merged current-main createSseByteGuard helper and this provider-path follow-up that reuses it. (role: adjacent helper contributor; confidence: high; commits: 5880e0afc4dc, 7c3dea35e1b2; files: src/agents/streaming-byte-guard.ts, src/llm/providers/openai-chatgpt-responses.ts, src/llm/providers/anthropic.ts)
  • steipete: Recent merged Anthropic request/tool-schema work touched the same provider module and request-building surface. (role: adjacent Anthropic runtime contributor; confidence: medium; commits: a02813164dd5; files: src/llm/providers/anthropic.ts, src/llm/providers/anthropic.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 25, 2026
@wangmiao0668000666
wangmiao0668000666 force-pushed the fix/anthropic-provider-stream-bound branch from dd7819a to 3407ee1 Compare June 25, 2026 17:25
@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Rebased to latest main (fixes CI). Upgraded proof to 🦞-grade: added BEFORE (unbounded) comparison showing 64.0 MiB OOM vector confirmed (4× the cap), negative control with bytes-sent quantification, sibling PR references (#96772, #96782, #96777), and AI disclosure.

@clawsweeper

clawsweeper Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

vincentkoc pushed a commit to wangmiao0668000666/openclaw that referenced this pull request Jun 27, 2026
… at 16 MiB

Apply createSseByteGuard to src/llm/providers/openai-chatgpt-responses.ts
(parseSSE) so the ChatGPT Responses SSE parser cannot be exhausted by a
hostile or malfunctioning endpoint that streams an unbounded SSE body.
The 16 MiB cap matches the non-streaming readProviderJsonResponse cap.

What changed:
- src/llm/providers/openai-chatgpt-responses.ts: wrap body.getReader() in
  createSseByteGuard before the existing parseSSE loop. The function is
  also re-exported as parseSSEForTest for direct test access.
- Inline test added to openai-chatgpt-responses.test.ts: hostile 1 MiB
  pull stream, asserts canonical overflow message, cancel(reason) was an
  Error instance, pullCount bounded to 17-20.

Reuses the createSseByteGuard helper from src/agents/streaming-byte-guard.ts
(introduced in openclaw#96701 for the anthropic-transport-stream path; same
helper, same 16 MiB cap, same overflow-error shape). This PR is the
third of the planned per-surface rescue series for the previously closed
PR openclaw#96666, after PR #3b (openclaw#96723).

No SDK surface change. No repro script committed (proof in this body).
vincentkoc pushed a commit to wangmiao0668000666/openclaw that referenced this pull request Jun 27, 2026
… at 16 MiB

Apply createSseByteGuard to src/llm/providers/openai-chatgpt-responses.ts
(parseSSE) so the ChatGPT Responses SSE parser cannot be exhausted by a
hostile or malfunctioning endpoint that streams an unbounded SSE body.
The 16 MiB cap matches the non-streaming readProviderJsonResponse cap.

What changed:
- src/llm/providers/openai-chatgpt-responses.ts: wrap body.getReader() in
  createSseByteGuard before the existing parseSSE loop. The function is
  also re-exported as parseSSEForTest for direct test access.
- Inline test added to openai-chatgpt-responses.test.ts: hostile 1 MiB
  pull stream, asserts canonical overflow message, cancel(reason) was an
  Error instance, pullCount bounded to 17-20.

Reuses the createSseByteGuard helper from src/agents/streaming-byte-guard.ts
(introduced in openclaw#96701 for the anthropic-transport-stream path; same
helper, same 16 MiB cap, same overflow-error shape). This PR is the
third of the planned per-surface rescue series for the previously closed
PR openclaw#96666, after PR #3b (openclaw#96723).

No SDK surface change. No repro script committed (proof in this body).
vincentkoc added a commit that referenced this pull request Jun 27, 2026
… at 16 MiB (#96762)

* fix(openai-chatgpt-responses): bound streaming success-body SSE reads at 16 MiB

Apply createSseByteGuard to src/llm/providers/openai-chatgpt-responses.ts
(parseSSE) so the ChatGPT Responses SSE parser cannot be exhausted by a
hostile or malfunctioning endpoint that streams an unbounded SSE body.
The 16 MiB cap matches the non-streaming readProviderJsonResponse cap.

What changed:
- src/llm/providers/openai-chatgpt-responses.ts: wrap body.getReader() in
  createSseByteGuard before the existing parseSSE loop. The function is
  also re-exported as parseSSEForTest for direct test access.
- Inline test added to openai-chatgpt-responses.test.ts: hostile 1 MiB
  pull stream, asserts canonical overflow message, cancel(reason) was an
  Error instance, pullCount bounded to 17-20.

Reuses the createSseByteGuard helper from src/agents/streaming-byte-guard.ts
(introduced in #96701 for the anthropic-transport-stream path; same
helper, same 16 MiB cap, same overflow-error shape). This PR is the
third of the planned per-surface rescue series for the previously closed
PR #96666, after PR #3b (#96723).

No SDK surface change. No repro script committed (proof in this body).

* fix(openai): bound ChatGPT error body reads

* fix(openai): bound chatgpt error parsing

---------

Co-authored-by: Vincent Koc <[email protected]>
wangmiao0668000666 added a commit to wangmiao0668000666/openclaw that referenced this pull request Jun 27, 2026
… at 16 MiB (openclaw#96762)

* fix(openai-chatgpt-responses): bound streaming success-body SSE reads at 16 MiB

Apply createSseByteGuard to src/llm/providers/openai-chatgpt-responses.ts
(parseSSE) so the ChatGPT Responses SSE parser cannot be exhausted by a
hostile or malfunctioning endpoint that streams an unbounded SSE body.
The 16 MiB cap matches the non-streaming readProviderJsonResponse cap.

What changed:
- src/llm/providers/openai-chatgpt-responses.ts: wrap body.getReader() in
  createSseByteGuard before the existing parseSSE loop. The function is
  also re-exported as parseSSEForTest for direct test access.
- Inline test added to openai-chatgpt-responses.test.ts: hostile 1 MiB
  pull stream, asserts canonical overflow message, cancel(reason) was an
  Error instance, pullCount bounded to 17-20.

Reuses the createSseByteGuard helper from src/agents/streaming-byte-guard.ts
(introduced in openclaw#96701 for the anthropic-transport-stream path; same
helper, same 16 MiB cap, same overflow-error shape). This PR is the
third of the planned per-surface rescue series for the previously closed
PR openclaw#96666, after PR #3b (openclaw#96723).

No SDK surface change. No repro script committed (proof in this body).

* fix(openai): bound ChatGPT error body reads

* fix(openai): bound chatgpt error parsing

---------

Co-authored-by: Vincent Koc <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 27, 2026
… at 16 MiB (openclaw#96762)

* fix(openai-chatgpt-responses): bound streaming success-body SSE reads at 16 MiB

Apply createSseByteGuard to src/llm/providers/openai-chatgpt-responses.ts
(parseSSE) so the ChatGPT Responses SSE parser cannot be exhausted by a
hostile or malfunctioning endpoint that streams an unbounded SSE body.
The 16 MiB cap matches the non-streaming readProviderJsonResponse cap.

What changed:
- src/llm/providers/openai-chatgpt-responses.ts: wrap body.getReader() in
  createSseByteGuard before the existing parseSSE loop. The function is
  also re-exported as parseSSEForTest for direct test access.
- Inline test added to openai-chatgpt-responses.test.ts: hostile 1 MiB
  pull stream, asserts canonical overflow message, cancel(reason) was an
  Error instance, pullCount bounded to 17-20.

Reuses the createSseByteGuard helper from src/agents/streaming-byte-guard.ts
(introduced in openclaw#96701 for the anthropic-transport-stream path; same
helper, same 16 MiB cap, same overflow-error shape). This PR is the
third of the planned per-surface rescue series for the previously closed
PR openclaw#96666, after PR #3b (openclaw#96723).

No SDK surface change. No repro script committed (proof in this body).

* fix(openai): bound ChatGPT error body reads

* fix(openai): bound chatgpt error parsing

---------

Co-authored-by: Vincent Koc <[email protected]>
…(provider path)

Apply createSseByteGuard to src/llm/providers/anthropic.ts (legacy provider
iterateSseMessages) so the Anthropic Messages provider SSE parser cannot
be exhausted by a hostile or malfunctioning Anthropic-compatible endpoint
that streams an unbounded SSE body. The 16 MiB cap matches the
non-streaming readProviderJsonResponse cap.

What changed:
- src/llm/providers/anthropic.ts: wrap body.getReader() in
  createSseByteGuard before the existing iterateSseMessages loop. The
  function is also re-exported as iterateSseMessagesForTest for direct
  test access.
- Inline test added to anthropic.test.ts: hostile 1 MiB pull stream,
  asserts canonical overflow message, cancel(reason) was an Error
  instance, pullCount bounded to 17-20.

Reuses the createSseByteGuard helper from src/agents/streaming-byte-guard.ts
(introduced in openclaw#96701 for the anthropic-transport-stream path; same
helper, same 16 MiB cap, same overflow-error shape). This PR is the
second of the planned per-surface rescue series for the previously
closed PR openclaw#96632, after openclaw#96701.

No SDK surface change. No repro script committed (proof in this body).
The companion error-body path is already bounded (readAnthropicMessages
ErrorBodySnippet, 8 KiB + 10s idle timeout, openclaw#95108); this PR closes the
legacy-provider success-body gap.
@wangmiao0668000666
wangmiao0668000666 force-pushed the fix/anthropic-provider-stream-bound branch from 3407ee1 to 7c3dea3 Compare June 27, 2026 16:06
@openclaw-barnacle openclaw-barnacle Bot removed the agents Agent runtime and tooling label Jun 27, 2026
@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Rebased onto current main. The createSseByteGuard helper (originally 87 LoC in this PR) is now in main (via #96762 merge), so the diff no longer includes the helper creation — only the application in iterateSseMessages (legacy provider path). Body updated to "Reuses the existing helper from main".

Diff: 2 files, +59/-2 (no more streaming-byte-guard.ts creation). Test count: 37/37.

@clawsweeper

clawsweeper Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

xydigit-zt pushed a commit to xydigit-zt/xydigit-zt-openclaw that referenced this pull request Jun 28, 2026
… at 16 MiB (openclaw#96762)

* fix(openai-chatgpt-responses): bound streaming success-body SSE reads at 16 MiB

Apply createSseByteGuard to src/llm/providers/openai-chatgpt-responses.ts
(parseSSE) so the ChatGPT Responses SSE parser cannot be exhausted by a
hostile or malfunctioning endpoint that streams an unbounded SSE body.
The 16 MiB cap matches the non-streaming readProviderJsonResponse cap.

What changed:
- src/llm/providers/openai-chatgpt-responses.ts: wrap body.getReader() in
  createSseByteGuard before the existing parseSSE loop. The function is
  also re-exported as parseSSEForTest for direct test access.
- Inline test added to openai-chatgpt-responses.test.ts: hostile 1 MiB
  pull stream, asserts canonical overflow message, cancel(reason) was an
  Error instance, pullCount bounded to 17-20.

Reuses the createSseByteGuard helper from src/agents/streaming-byte-guard.ts
(introduced in openclaw#96701 for the anthropic-transport-stream path; same
helper, same 16 MiB cap, same overflow-error shape). This PR is the
third of the planned per-surface rescue series for the previously closed
PR openclaw#96666, after PR #3b (openclaw#96723).

No SDK surface change. No repro script committed (proof in this body).

* fix(openai): bound ChatGPT error body reads

* fix(openai): bound chatgpt error parsing

---------

Co-authored-by: Vincent Koc <[email protected]>
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
… at 16 MiB (openclaw#96762)

* fix(openai-chatgpt-responses): bound streaming success-body SSE reads at 16 MiB

Apply createSseByteGuard to src/llm/providers/openai-chatgpt-responses.ts
(parseSSE) so the ChatGPT Responses SSE parser cannot be exhausted by a
hostile or malfunctioning endpoint that streams an unbounded SSE body.
The 16 MiB cap matches the non-streaming readProviderJsonResponse cap.

What changed:
- src/llm/providers/openai-chatgpt-responses.ts: wrap body.getReader() in
  createSseByteGuard before the existing parseSSE loop. The function is
  also re-exported as parseSSEForTest for direct test access.
- Inline test added to openai-chatgpt-responses.test.ts: hostile 1 MiB
  pull stream, asserts canonical overflow message, cancel(reason) was an
  Error instance, pullCount bounded to 17-20.

Reuses the createSseByteGuard helper from src/agents/streaming-byte-guard.ts
(introduced in openclaw#96701 for the anthropic-transport-stream path; same
helper, same 16 MiB cap, same overflow-error shape). This PR is the
third of the planned per-surface rescue series for the previously closed
PR openclaw#96666, after PR #3b (openclaw#96723).

No SDK surface change. No repro script committed (proof in this body).

* fix(openai): bound ChatGPT error body reads

* fix(openai): bound chatgpt error parsing

---------

Co-authored-by: Vincent Koc <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
… at 16 MiB (openclaw#96762)

* fix(openai-chatgpt-responses): bound streaming success-body SSE reads at 16 MiB

Apply createSseByteGuard to src/llm/providers/openai-chatgpt-responses.ts
(parseSSE) so the ChatGPT Responses SSE parser cannot be exhausted by a
hostile or malfunctioning endpoint that streams an unbounded SSE body.
The 16 MiB cap matches the non-streaming readProviderJsonResponse cap.

What changed:
- src/llm/providers/openai-chatgpt-responses.ts: wrap body.getReader() in
  createSseByteGuard before the existing parseSSE loop. The function is
  also re-exported as parseSSEForTest for direct test access.
- Inline test added to openai-chatgpt-responses.test.ts: hostile 1 MiB
  pull stream, asserts canonical overflow message, cancel(reason) was an
  Error instance, pullCount bounded to 17-20.

Reuses the createSseByteGuard helper from src/agents/streaming-byte-guard.ts
(introduced in openclaw#96701 for the anthropic-transport-stream path; same
helper, same 16 MiB cap, same overflow-error shape). This PR is the
third of the planned per-surface rescue series for the previously closed
PR openclaw#96666, after PR #3b (openclaw#96723).

No SDK surface change. No repro script committed (proof in this body).

* fix(openai): bound ChatGPT error body reads

* fix(openai): bound chatgpt error parsing

---------

Co-authored-by: Vincent Koc <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
… at 16 MiB (openclaw#96762)

* fix(openai-chatgpt-responses): bound streaming success-body SSE reads at 16 MiB

Apply createSseByteGuard to src/llm/providers/openai-chatgpt-responses.ts
(parseSSE) so the ChatGPT Responses SSE parser cannot be exhausted by a
hostile or malfunctioning endpoint that streams an unbounded SSE body.
The 16 MiB cap matches the non-streaming readProviderJsonResponse cap.

What changed:
- src/llm/providers/openai-chatgpt-responses.ts: wrap body.getReader() in
  createSseByteGuard before the existing parseSSE loop. The function is
  also re-exported as parseSSEForTest for direct test access.
- Inline test added to openai-chatgpt-responses.test.ts: hostile 1 MiB
  pull stream, asserts canonical overflow message, cancel(reason) was an
  Error instance, pullCount bounded to 17-20.

Reuses the createSseByteGuard helper from src/agents/streaming-byte-guard.ts
(introduced in openclaw#96701 for the anthropic-transport-stream path; same
helper, same 16 MiB cap, same overflow-error shape). This PR is the
third of the planned per-surface rescue series for the previously closed
PR openclaw#96666, after PR #3b (openclaw#96723).

No SDK surface change. No repro script committed (proof in this body).

* fix(openai): bound ChatGPT error body reads

* fix(openai): bound chatgpt error parsing

---------

Co-authored-by: Vincent Koc <[email protected]>
@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

Closing as duplicate of #96701 / superseded by #96762.

Decision: This PR and #96701 both bound Anthropic streaming SSE at 16 MiB via createSseByteGuard. The shared helper landed via #96762 (openai-chatgpt-responses) on 2026-06-27, so the helper-creation diff in this PR is no longer needed. The remaining per-provider call-site addition is now a one-line wrap.

Why: 14 days open with no maintainer movement; the broader 16 MiB body-bound initiative (#96680 cluster) was closed out on 2026-06-29 in a maintainer batch that did not select this branch.

Supported alternative: A one-line follow-up PR that imports createSseByteGuard from the now-merged shared helper and wraps the Anthropic provider-path 200 success-body stream — small enough that a fresh reviewer can land it cleanly. Happy to draft this if you want a focused PR.

Evidence that would change the decision: a maintainer explicitly asks for this exact branch to land (vs. the follow-up), or a new finding that the helper exported by #96762 does not actually fit the Anthropic provider path.

🤖 Generated with Claude Code

wheakerd pushed a commit to wheakerd/clawdbot that referenced this pull request Jul 15, 2026
… at 16 MiB (openclaw#96762)

* fix(openai-chatgpt-responses): bound streaming success-body SSE reads at 16 MiB

Apply createSseByteGuard to src/llm/providers/openai-chatgpt-responses.ts
(parseSSE) so the ChatGPT Responses SSE parser cannot be exhausted by a
hostile or malfunctioning endpoint that streams an unbounded SSE body.
The 16 MiB cap matches the non-streaming readProviderJsonResponse cap.

What changed:
- src/llm/providers/openai-chatgpt-responses.ts: wrap body.getReader() in
  createSseByteGuard before the existing parseSSE loop. The function is
  also re-exported as parseSSEForTest for direct test access.
- Inline test added to openai-chatgpt-responses.test.ts: hostile 1 MiB
  pull stream, asserts canonical overflow message, cancel(reason) was an
  Error instance, pullCount bounded to 17-20.

Reuses the createSseByteGuard helper from src/agents/streaming-byte-guard.ts
(introduced in openclaw#96701 for the anthropic-transport-stream path; same
helper, same 16 MiB cap, same overflow-error shape). This PR is the
third of the planned per-surface rescue series for the previously closed
PR openclaw#96666, after PR #3b (openclaw#96723).

No SDK surface change. No repro script committed (proof in this body).

* fix(openai): bound ChatGPT error body reads

* fix(openai): bound chatgpt error parsing

---------

Co-authored-by: Vincent Koc <[email protected]>
(cherry picked from commit 5880e0a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant