Skip to content

fix(openai,proxy): bound streaming 200 success-body SSE reads via shared internal guard#96666

Closed
wangmiao0668000666 wants to merge 2 commits into
openclaw:mainfrom
wangmiao0668000666:fix/openai-proxy-stream-bound-read
Closed

fix(openai,proxy): bound streaming 200 success-body SSE reads via shared internal guard#96666
wangmiao0668000666 wants to merge 2 commits into
openclaw:mainfrom
wangmiao0668000666:fix/openai-proxy-stream-bound-read

Conversation

@wangmiao0668000666

Copy link
Copy Markdown
Contributor

What Problem This Solves

src/llm/providers/openai-chatgpt-responses.ts and src/agents/runtime/proxy.ts accumulate an unbounded SSE byte stream into a string buffer while waiting for frame delimiters. Both surfaces are reachable from every ChatGPT Responses model call (openai-chatgpt-responses API, used by the OpenAI Codex Responses plugin and downstream proxies). A hostile or malfunctioning upstream — including a hijacked ChatGPT Responses mirror, a custom proxy backend that does not bound its streams, or a self-hosted LLM mirror serving the OpenAI Codex protocol — can return a Content-Length-less SSE body that streams forever, exhausting process memory on a code path that runs for every /v1/messages or proxied-stream call.

This is the same OOM family as #96632 (Anthropic SSE success-body bound) and Alix-007's bound-stream sweep (#95218, #95223, #96027, #96035, #96038, #96042): same 16 MiB cap, same helper-driven design, but on the two remaining core streaming-body surfaces that read response.body.getReader() without an accumulation cap.

Stacks on fix/anthropic-stream-bound-read (#96632). Reuses the existing src/agents/streaming-byte-guard.ts helper that PR #96632 introduced as a core-internal helper. No new public SDK subpath, no SDK metadata drift.

Changes

  • src/llm/providers/openai-chatgpt-responses.ts:719parseSSE now wraps response.body.getReader() in createSseByteGuard with OPENAI_CODEX_RESPONSES_SUCCESS_BODY_MAX_BYTES = 16 * 1024 * 1024. Existing \n\n frame parsing, JSON.parse failure wrapping in CodexProtocolError, and reader.releaseLock() cleanup are unchanged. Also exports the function as parseSSEForTest for direct testing.
  • src/agents/runtime/proxy.ts:194streamProxy now wraps response.body!.getReader() in createSseByteGuard with PROXY_SUCCESS_BODY_MAX_BYTES = 16 * 1024 * 1024. The abort handler at line 158 also routes through guard.cancel() so a bounded cancel propagates immediately on signal abort (with a fallback to reader.cancel() for the brief window before the guard is constructed). Existing \n line splitting, terminal-event tracking, and signal-abort checks are unchanged.
  • src/llm/providers/openai-chatgpt-responses.test.ts — new describe("parseSSEForTest (streaming 200 success-body bound)") block:
    1. bounds streamed OpenAI Codex Responses success bodies without content-length drives parseSSEForTest against a hostile 1 MiB-per-pull streaming body via onPull/onCancel callbacks, asserts the canonical overflow message (OpenAI Codex Responses success body exceeded 16777216 bytes), confirms cancel(reason) was invoked with an Error instance, and verifies pull count is bounded (17–20 pulls, well below the unconstrained ceiling).
    2. parses normal OpenAI Codex Responses SSE responses end-to-end ensures small valid SSE frames still parse through the bounded reader unchanged.
  • src/agents/runtime/proxy.test.ts — new integration test bounds streamed proxy success bodies without content-length stubs fetch with a hostile 1 MiB-per-pull ReadableStream body, drives streamProxy, asserts the stream ends with a bounded error containing Proxy success body exceeded 16777216 bytes, and verifies pull count is bounded (17–20 pulls).

Real behavior proof

  • Behavior addressed: unbounded buffering of the OpenAI Codex Responses streaming 200 success-body SSE read and the runtime proxy streaming 200 success-body SSE read; after the fix both reads are capped at 16 MiB and the streams are 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 with backpressure, plus a small (8 MiB) negative-control body, plus a happy-path body, plus a legacy-path parity check driving the production parseSSEForTest. Run with pnpm exec tsx scripts/repro/issue-openai-proxy-sse-bound-read.mjs.
  • Exact steps:
    • node scripts/run-vitest.mjs openai-chatgpt-responses proxy.test anthropic-transport-stream anthropic.test --run — 84/84 passed (4 files; 2 new tests in openai-chatgpt-responses.test.ts, 1 new test in proxy.test.ts, 79 pre-existing tests in the two anthropic files untouched by this PR)
    • pnpm exec tsx scripts/repro/issue-openai-proxy-sse-bound-read.mjs — 5/5 PASS
    • node scripts/run-tsgo.mjs — clean
    • node scripts/run-oxlint.mjs — clean
    • node scripts/sync-plugin-sdk-exports.mjs --checkplugin-sdk exports synced.
  • Evidence after fix:
    PASS  OpenAI Codex Responses success body bounded: rejected with "OpenAI Codex Responses success body exceeded 16777216 bytes (received 16820142)..."; bytesSent=19922944 (< 67108864); server.aborted=true
    PASS  cap-trace: bounded reader cancelled at ~16771374 bytes (full body = 67108864); server.aborted=true
    PASS  negative control: small body fully drained (8388608 bytes >= 8388608); bounded reader saw 8388608 bytes; cap did not trigger; aborted=false
    PASS  happy path: small valid OpenAI Codex Responses SSE response drained end-to-end (246 bytes <= 16777216)
    PASS  parseSSEForTest overflow surfaces "OpenAI Codex Responses success body exceeded 16777216 bytes (received 16835008)..."; bytesSent=18874368; server.aborted=true
    
  • Observed result after fix: createSseByteGuard cancelled the upstream ReadableStreamDefaultReader after ~17–19 MiB (≪ 64 MiB) for both bounded readers. Server-side bytesSent observed at 18,874,368 — bounded, not drained. Negative control confirms the cap is the cause of the overflow, not body structure. Legacy-path parity confirms the production parseSSE rejects with the canonical error.
  • What was not tested: live OpenAI Codex Responses API call or live proxy backend (this proof exercises the same production helper against the same shape of attack the live endpoint could carry); cross-platform Node 18/20 differences (Node 22 only, matches CI).

Out of scope (separate PR)

  • extensions/google/transport-stream.ts + extensions/ollama/src/stream.ts — both extensions consume the same ReadableStreamDefaultReader<Uint8Array> pattern and will need the same 16 MiB cap. This is a separate PR because extensions cannot import src/agents/** per the boundary rule in src/plugin-sdk/CLAUDE.md; the helper will need to be promoted back through the public plugin-SDK seam with full SDK metadata synchronization when that PR is opened.

Risk

Diff vs PR #96632 (its base)

 scripts/repro/issue-openai-proxy-sse-bound-read.mjs | 415 ++++++++++++++
 src/agents/runtime/proxy.test.ts                    |  38 +++
 src/agents/runtime/proxy.ts                         |  17 +++ / 2 --
 src/llm/providers/openai-chatgpt-responses.test.ts  |  96 +++++++++++
 src/llm/providers/openai-chatgpt-responses.ts       |  22 +++ / 2 --
 5 files changed, 588 insertions(+), 4 deletions(-)

The streaming-byte-guard.ts helper and its consumer files in src/agents/anthropic-transport-stream.ts + src/llm/providers/anthropic.ts come from PR #96632 (stacked, not duplicated).

… internal guard

ClawSweeper review follow-up: revert stream-reader-with-limit from a new
public plugin-SDK subpath to an internal helper under src/agents/. The new
subpath required maintainer API-boundary acceptance plus regenerated SDK
metadata; deferring that to the extension follow-up PRs (extensions/google
+ extensions/ollama) keeps this PR a clean bounded-refactor with no public
SDK surface change.

The helper now lives at src/agents/streaming-byte-guard.ts, mirroring the
existing readResponseWithLimit / readProviderJsonResponse family in
src/agents/provider-http-errors.ts. Both Anthropic SSE parsers (the new
src/agents/anthropic-transport-stream.ts parser and the legacy
src/llm/providers/anthropic.ts iterateSseMessages) consume the helper via
relative imports, no plugin-sdk boundary involved.

Removal summary:
- DELETE src/plugin-sdk/stream-reader-with-limit.ts
- CREATE src/agents/streaming-byte-guard.ts (rename with comment update)
- UPDATE src/agents/anthropic-transport-stream.ts (relative import)
- UPDATE src/llm/providers/anthropic.ts (relative import)
- UPDATE scripts/repro/issue-anthropic-sse-bound-read.mjs (relative path)
- REMOVE entry from scripts/lib/plugin-sdk-entrypoints.json (line 314)
- REMOVE ./plugin-sdk/stream-reader-with-limit export from package.json
- REMOVE row from docs/plugins/sdk-subpaths.md
- REMOVE two now-unused @ts-expect-error directives in anthropic-transport-stream.test.ts

Re-verification:
- vitest: 115/115 (src/agents/anthropic-transport-stream.test.ts + src/llm/providers/anthropic.test.ts)
- repro script: 5/5 PASS (real node:http server, 64 MiB content-length-less body)
- tsgo: clean
- oxlint: clean
- plugin-sdk:check-exports: synced
- docs/.generated/plugin-sdk-api-baseline.sha256: unchanged (SDK surface restored)

Closes ClawSweeper P1 blockers from review on this PR: new public SDK subpath
requires maintainer acceptance (deferred), SDK metadata drift (resolved by
removal).
@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts agents Agent runtime and tooling size: XL labels Jun 25, 2026
@clawsweeper

clawsweeper Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Keep open: current main still has the unbounded OpenAI Codex Responses and proxy SSE success-body reads, and the PR is a plausible internal hardening path with sufficient real behavior proof. It is not merge-ready because the latest head still fails the scripts lint shard, the helper stack depends on open related work, and the new 16 MiB cutoff is a compatibility-sensitive behavior change.

Canonical path: Close this PR as superseded by #96632.

So I’m closing this here and keeping the remaining discussion on #96632.

Review details

Best possible solution:

Close this PR as superseded by #96632.

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

Yes. Current-main source shows both target SSE paths append streaming chunks without a total byte cap, and the PR body includes real local HTTP oversized-stream proof showing cancellation after the fix.

Is this the best way to solve the issue?

Mostly yes. The internal guard matches the existing bounded-response pattern and covers both core call sites, but the lint failure, open stack dependency, and cap acceptance remain before merge.

Security review:

Security review cleared: The diff reduces an unbounded external-response availability risk and does not add dependency, workflow, lockfile, secret-handling, downloaded-artifact, or public SDK-surface concerns.

AGENTS.md: found and applied where relevant.

What I checked:

  • linked superseding PR: fix(anthropic): bound streaming 200 success-body SSE reads via shared plugin-sdk guard #96632 (fix(anthropic): bound streaming 200 success-body SSE reads via shared plugin-sdk guard) is still open as the canonical replacement.
  • cluster evidence: the durable review links that PR in the work cluster or recommended risk path.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • Shakker: Current-main blame for both target streaming loops points to commit a49816f; this is an area-routing signal for the current code path. (role: recent area contributor; confidence: medium; commits: a49816ffbb52; files: src/llm/providers/openai-chatgpt-responses.ts, src/agents/runtime/proxy.ts)
  • joshavant: Recent work added the shared provider response limit precedent and 16 MiB provider response cap family that this PR mirrors for SSE reads. (role: adjacent bounded-response contributor; confidence: medium; commits: 0a14444924e3; files: src/agents/provider-http-errors.ts, packages/media-core/src/read-response-with-limit.ts)
  • Alix-007: Merged related response-limit PRs cited by this PR as the same bounded-read campaign for provider success bodies. (role: adjacent response-limit campaign contributor; confidence: medium; commits: 605aede38c10, 7844b0844568; files: extensions/exa/src, extensions/lmstudio/src)

Codex review notes: model internal, reasoning high; reviewed against 56d95b18f4bc.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. 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
…red internal guard

Same OOM family as PR openclaw#96632 (Anthropic SSE success-body bound), applied to the
two remaining core streaming-body surfaces that read response.body.getReader()
without an accumulation cap:

  - src/llm/providers/openai-chatgpt-responses.ts (parseSSE, ChatGPT Responses)
  - src/agents/runtime/proxy.ts (streamProxy, internal proxy route)

Both surfaces accumulate decoder-decoded bytes into a string buffer while
waiting for the next frame delimiter, with no upper bound. A hostile or
malfunctioning endpoint (ChatGPT Responses, custom proxy backend) can return
a Content-Length-less SSE body that streams forever, exhausting process memory
on every /v1/messages or proxied-stream call.

Fix: wrap the existing response.body.getReader() in the shared
src/agents/streaming-byte-guard.ts createSseByteGuard helper (introduced in
openclaw#96632, now reused). 16 MiB cap, matching readProviderJsonResponse. The
existing chunk-by-chunk parsing loop, frame delimiter handling, and signal
abort checks are unchanged.

For proxy.ts the abort handler also routes through guard.cancel() so the
bounded cancel propagates immediately on signal abort (with a fallback to
reader.cancel() for the brief window before the guard is constructed).

Tests:
  - src/llm/providers/openai-chatgpt-responses.test.ts: new describe block
    drives parseSSEForTest (renamed from parseSSE for direct testing) against
    a hostile 1 MiB-per-pull streaming body, asserts canonical overflow message
    and bounded pullCount (17-20 pulls), plus a happy-path end-to-end parse.
  - src/agents/runtime/proxy.test.ts: new integration test stubs fetch with
    a hostile ReadableStream body, asserts the stream ends with the bounded
    error and pullCount is bounded.

Real-environment proof: scripts/repro/issue-openai-proxy-sse-bound-read.mjs
runs against a real node:http server bound to 127.0.0.1 that streams a
Content-Length-less 64 MiB body chunk-by-chunk. Five PASS lines:
  1. bounded rejection: bytesSent ~19 MiB (<< 64 MiB), server.aborted=true
  2. cap-trace: bounded reader cancelled at ~16 MiB
  3. negative control: small body fully drained, no cap
  4. happy path: small valid OpenAI SSE drained end-to-end
  5. legacy-path parity: parseSSEForTest overflow surfaces canonical error

Stacked on fix/anthropic-stream-bound-read (openclaw#96632); no new public SDK
subpath, no SDK metadata drift, no plugin boundary touched.

Stacked-on: fix/anthropic-stream-bound-read
@wangmiao0668000666
wangmiao0668000666 force-pushed the fix/openai-proxy-stream-bound-read branch from 3be9e87 to 7da6061 Compare June 25, 2026 06:14
@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Fixed all 10 lint errors from your first review on the new repro script. The strict scripts lint config (config/tsconfig/oxlint.scripts.json) was catching issues my pre-flight --tsconfig tsconfig.json run missed.

Categories fixed:

  • eslint(curly) × 3 — added { } around single-line if bodies
  • eslint(no-promise-executor-return) × 4 — changed inline await new Promise((resolve) => fn(resolve)) to block-body await new Promise((resolve) => { fn(resolve); }) so the executor has no implicit return
  • eslint(no-unused-vars) × 1 — removed unused stats destructure in the happy-path case
  • eslint(no-underscore-dangle) × 1 — replaced for await (const _event of …) with for await (const event of …) { expect(event).toBeDefined(); } (matches the same pattern PR fix(anthropic): bound streaming 200 success-body SSE reads via shared plugin-sdk guard #96632 used)
  • typescript(use-unknown-in-catch-callback-variable) × 1 — removed a leftover : unknown annotation that wouldn't compile in .mjs

Pre-flight now all green:

  • npx oxlint --tsconfig config/tsconfig/oxlint.scripts.json scripts/repro/issue-openai-proxy-sse-bound-read.mjs — exit 0
  • node scripts/run-oxlint-shards.mjs --threads=4 — exit 0 (full lint suite: core + extensions + scripts)
  • node scripts/run-vitest.mjs openai-chatgpt-responses proxy.test anthropic-transport-stream anthropic.test --run — 84/84 passed
  • pnpm exec tsx scripts/repro/issue-openai-proxy-sse-bound-read.mjs — 5/5 PASS
  • node scripts/run-tsgo.mjs — exit 0

On the other ClawSweeper points (not requiring code changes, addressed in PR body):

Force-pushed as 7da6061208 — re-triggering CI to confirm check-lint is now green.

@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

Closing in favor of the Alix-007-style XS split.

Why: This PR's diff (L size, 10 files, 1131 LoC) and the "stacked on #96632" structure compound the review risk Alix-007's per-surface pattern avoids. ClawSweeper's gold-shrimp verdict flagged the stacking explicitly: "The PR is stacked on open PR #96632 for the shared internal guard and should land after that prerequisite."

What happens to this work:

Structure after split:

  • Foundation PR (XS): promote helper to openclaw/plugin-sdk/stream-reader-with-limit, update 4 core consumers' imports.
  • google PR (XS): apply SDK helper to extensions/google/transport-stream.ts.
  • ollama PR (XS): apply SDK helper to extensions/ollama/src/{setup,stream}.ts.

The new structure removes the stacking dependency and brings each surface into Alix-007's bounded-read family template.

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]>
wangmiao0668000666 added a commit to wangmiao0668000666/openclaw that referenced this pull request Jun 27, 2026
Apply createSseByteGuard to src/agents/runtime/proxy.ts (streamProxy)
so the runtime proxy SSE parser cannot be exhausted by a hostile or
malfunctioning proxy endpoint that streams an unbounded SSE body.
The 16 MiB cap matches the non-streaming readProviderJsonResponse cap.

What changed:
- src/agents/runtime/proxy.ts: wrap response.body.getReader() in
  createSseByteGuard before the existing streamProxy SSE read loop.
  The abort handler and finally block use guard.cancel() instead of
  reader.cancel().
- Inline test added to proxy.test.ts: hostile 1 MiB pull stream through
  streamProxy, asserts errorMessage matches overflow pattern,
  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
final per-surface rescue from the previously closed PR openclaw#96666, after
PR #3c (openclaw#96762).

No SDK surface change. No repro script committed (proof in this body).
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]>
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

agents Agent runtime and tooling 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: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. scripts Repository scripts size: XL status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant