fix(provider-transport-fetch): bound SSE buffer to prevent OOM#96989
Conversation
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Codex review: needs maintainer review before merge. Reviewed June 26, 2026, 8:50 PM ET / 00:50 UTC. Summary PR surface: Source +31, Tests +127. Total +158 across 2 files. Reproducibility: yes. Current main source shows delimiter-free SSE and streaming JSON responses can accumulate without a cap, and the PR body includes after-fix terminal output from a real Review metrics: 1 noteworthy metric.
Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the focused caps after maintainers accept the fail-closed thresholds, or tune the constants first if known supported providers need larger payloads. Do we have a high-confidence way to reproduce the issue? Yes. Current main source shows delimiter-free SSE and streaming JSON responses can accumulate without a cap, and the PR body includes after-fix terminal output from a real Is this the best way to solve the issue? Yes, with a maintainer threshold decision. The patch caps the exact accumulators, preserves complete SSE event framing, and cancels upstream readers on overflow; a shared byte-guard helper is a cleanup alternative, not required for this fix. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 5880e0afc4dc. Label changesLabel justifications:
Evidence reviewedPR surface: Source +31, Tests +127. Total +158 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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 re-review P1 findings addressed:
Commit: 521e302 |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Removed unused @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
…hunk test Co-Authored-By: Claude <[email protected]>
261c96e to
b8c7ae1
Compare
…law#96989) * fix(provider-transport-fetch): bound SSE buffer to prevent OOM * fix(provider-transport-fetch): appease oxlint curly rule in test * fix(provider-transport-fetch): drain events before cap + cancel reader on overflow * fix(provider-transport-fetch): remove unused encoder from coalesced chunk test Co-Authored-By: Claude <[email protected]> * fix(transport): tighten SSE buffer guards --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Vincent Koc <[email protected]>
…law#96989) * fix(provider-transport-fetch): bound SSE buffer to prevent OOM * fix(provider-transport-fetch): appease oxlint curly rule in test * fix(provider-transport-fetch): drain events before cap + cancel reader on overflow * fix(provider-transport-fetch): remove unused encoder from coalesced chunk test Co-Authored-By: Claude <[email protected]> * fix(transport): tighten SSE buffer guards --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Vincent Koc <[email protected]>
…law#96989) * fix(provider-transport-fetch): bound SSE buffer to prevent OOM * fix(provider-transport-fetch): appease oxlint curly rule in test * fix(provider-transport-fetch): drain events before cap + cancel reader on overflow * fix(provider-transport-fetch): remove unused encoder from coalesced chunk test Co-Authored-By: Claude <[email protected]> * fix(transport): tighten SSE buffer guards --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Vincent Koc <[email protected]>
The 64 KiB inter-event SSE sanitize buffer added in openclaw#96989 rejects a single legitimate event larger than 64 KiB — e.g. a large gpt-5.5 reasoning summary on the openai-chatgpt-responses API — throwing "SSE response exceeded max buffer size (65536 bytes) without event boundary" and failing the whole request. The default ChatGPT-subscription gpt-5.5 path is unusable (present in v2026.6.11-beta.1). Decouple the two bounds: keep the non-OK error-body cap tight at 64 KiB (SSE_NONOK_BODY_MAX_BYTES), and raise the inter-event sanitize buffer to the same 16 MiB ceiling as the JSON-synthesis path. The guard still trips on a genuinely boundary-less (hostile/broken) stream, just not on a real large event. Co-Authored-By: Claude Opus 4.8 <[email protected]>
The 64 KiB inter-event SSE sanitize buffer added in #96989 rejects a single legitimate event larger than 64 KiB — e.g. a large gpt-5.5 reasoning summary on the openai-chatgpt-responses API — throwing "SSE response exceeded max buffer size (65536 bytes) without event boundary" and failing the whole request. The default ChatGPT-subscription gpt-5.5 path is unusable (present in v2026.6.11-beta.1). Decouple the two bounds: keep the non-OK error-body cap tight at 64 KiB (SSE_NONOK_BODY_MAX_BYTES), and raise the inter-event sanitize buffer to the same 16 MiB ceiling as the JSON-synthesis path. The guard still trips on a genuinely boundary-less (hostile/broken) stream, just not on a real large event. Co-Authored-By: Claude Opus 4.8 <[email protected]>
The 64 KiB inter-event SSE sanitize buffer added in openclaw#96989 rejects a single legitimate event larger than 64 KiB — e.g. a large gpt-5.5 reasoning summary on the openai-chatgpt-responses API — throwing "SSE response exceeded max buffer size (65536 bytes) without event boundary" and failing the whole request. The default ChatGPT-subscription gpt-5.5 path is unusable (present in v2026.6.11-beta.1). Decouple the two bounds: keep the non-OK error-body cap tight at 64 KiB (SSE_NONOK_BODY_MAX_BYTES), and raise the inter-event sanitize buffer to the same 16 MiB ceiling as the JSON-synthesis path. The guard still trips on a genuinely boundary-less (hostile/broken) stream, just not on a real large event. Co-Authored-By: Claude Opus 4.8 <[email protected]>
…law#96989) * fix(provider-transport-fetch): bound SSE buffer to prevent OOM * fix(provider-transport-fetch): appease oxlint curly rule in test * fix(provider-transport-fetch): drain events before cap + cancel reader on overflow * fix(provider-transport-fetch): remove unused encoder from coalesced chunk test Co-Authored-By: Claude <[email protected]> * fix(transport): tighten SSE buffer guards --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Vincent Koc <[email protected]>
The 64 KiB inter-event SSE sanitize buffer added in openclaw#96989 rejects a single legitimate event larger than 64 KiB — e.g. a large gpt-5.5 reasoning summary on the openai-chatgpt-responses API — throwing "SSE response exceeded max buffer size (65536 bytes) without event boundary" and failing the whole request. The default ChatGPT-subscription gpt-5.5 path is unusable (present in v2026.6.11-beta.1). Decouple the two bounds: keep the non-OK error-body cap tight at 64 KiB (SSE_NONOK_BODY_MAX_BYTES), and raise the inter-event sanitize buffer to the same 16 MiB ceiling as the JSON-synthesis path. The guard still trips on a genuinely boundary-less (hostile/broken) stream, just not on a real large event. Co-Authored-By: Claude Opus 4.8 <[email protected]>
…law#96989) * fix(provider-transport-fetch): bound SSE buffer to prevent OOM * fix(provider-transport-fetch): appease oxlint curly rule in test * fix(provider-transport-fetch): drain events before cap + cancel reader on overflow * fix(provider-transport-fetch): remove unused encoder from coalesced chunk test Co-Authored-By: Claude <[email protected]> * fix(transport): tighten SSE buffer guards --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Vincent Koc <[email protected]>
The 64 KiB inter-event SSE sanitize buffer added in openclaw#96989 rejects a single legitimate event larger than 64 KiB — e.g. a large gpt-5.5 reasoning summary on the openai-chatgpt-responses API — throwing "SSE response exceeded max buffer size (65536 bytes) without event boundary" and failing the whole request. The default ChatGPT-subscription gpt-5.5 path is unusable (present in v2026.6.11-beta.1). Decouple the two bounds: keep the non-OK error-body cap tight at 64 KiB (SSE_NONOK_BODY_MAX_BYTES), and raise the inter-event sanitize buffer to the same 16 MiB ceiling as the JSON-synthesis path. The guard still trips on a genuinely boundary-less (hostile/broken) stream, just not on a real large event. Co-Authored-By: Claude Opus 4.8 <[email protected]>
…law#96989) * fix(provider-transport-fetch): bound SSE buffer to prevent OOM * fix(provider-transport-fetch): appease oxlint curly rule in test * fix(provider-transport-fetch): drain events before cap + cancel reader on overflow * fix(provider-transport-fetch): remove unused encoder from coalesced chunk test Co-Authored-By: Claude <[email protected]> * fix(transport): tighten SSE buffer guards --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Vincent Koc <[email protected]>
The 64 KiB inter-event SSE sanitize buffer added in openclaw#96989 rejects a single legitimate event larger than 64 KiB — e.g. a large gpt-5.5 reasoning summary on the openai-chatgpt-responses API — throwing "SSE response exceeded max buffer size (65536 bytes) without event boundary" and failing the whole request. The default ChatGPT-subscription gpt-5.5 path is unusable (present in v2026.6.11-beta.1). Decouple the two bounds: keep the non-OK error-body cap tight at 64 KiB (SSE_NONOK_BODY_MAX_BYTES), and raise the inter-event sanitize buffer to the same 16 MiB ceiling as the JSON-synthesis path. The guard still trips on a genuinely boundary-less (hostile/broken) stream, just not on a real large event. Co-Authored-By: Claude Opus 4.8 <[email protected]>
…law#96989) * fix(provider-transport-fetch): bound SSE buffer to prevent OOM * fix(provider-transport-fetch): appease oxlint curly rule in test * fix(provider-transport-fetch): drain events before cap + cancel reader on overflow * fix(provider-transport-fetch): remove unused encoder from coalesced chunk test Co-Authored-By: Claude <[email protected]> * fix(transport): tighten SSE buffer guards --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Vincent Koc <[email protected]> (cherry picked from commit 1bccd29)
The 64 KiB inter-event SSE sanitize buffer added in openclaw#96989 rejects a single legitimate event larger than 64 KiB — e.g. a large gpt-5.5 reasoning summary on the openai-chatgpt-responses API — throwing "SSE response exceeded max buffer size (65536 bytes) without event boundary" and failing the whole request. The default ChatGPT-subscription gpt-5.5 path is unusable (present in v2026.6.11-beta.1). Decouple the two bounds: keep the non-OK error-body cap tight at 64 KiB (SSE_NONOK_BODY_MAX_BYTES), and raise the inter-event sanitize buffer to the same 16 MiB ceiling as the JSON-synthesis path. The guard still trips on a genuinely boundary-less (hostile/broken) stream, just not on a real large event. Co-Authored-By: Claude Opus 4.8 <[email protected]> (cherry picked from commit 81d60ca)
What Problem This Solves
src/agents/provider-transport-fetch.tshas two unbounded memory paths insanitizeOpenAISdkSseResponse:SSE sanitizer: Accumulates decoded chunks into a
bufferstring that only drains when a\n\nSSE event boundary is found. If data arrives without boundaries, the buffer grows unboundedly.JSON-to-SSE synthesis: Accumulates raw JSON bytes while synthesizing SSE frames, with no cumulative cap.
Non-OK SSE gap (fixed in this PR): Non-ok SSE responses previously bypassed ALL byte caps via an early
!response.okreturn, leaving the SDK error-bodyresponse.text()path unbounded — the same OOM vector.Changes
src/agents/provider-transport-fetch.ts— AddSSE_SYNTHESIZE_JSON_MAX_BYTES(16 MiB) andSSE_SANITIZE_BUFFER_MAX_BYTES(64 KiB). Cap the SSE sanitizer buffer after event-drain loop. Cap the JSON synthesis total bytes. Cap non-ok SSE bodies viacreateBoundedStream. Cancel upstream reader on overflow.src/agents/provider-transport-fetch.test.ts— 77 tests (was 73): add coalesced-valid-chunk regression test, oversized non-ok SSE regression test.createBoundedStreamis local to the file.Real behavior proof
sanitizeOpenAISdkSseResponseare now bounded. The SSE sanitizer buffer caps at 64 KiB between event boundaries. The JSON synthesis caps at 16 MiB total. Non-ok SSE bodies cap at 16 MiB.pnpm test src/agents/provider-transport-fetch.test.ts --runRisk checklist
PROVIDER_JSON_RESPONSE_MAX_BYTESprecedent.