Skip to content

fix(openai): bound embedding-batch and realtime session JSON respons#97533

Merged
vincentkoc merged 1 commit into
openclaw:mainfrom
hugenshen:fix/bound-openai-embedding-realtime-json-responses
Jun 28, 2026
Merged

fix(openai): bound embedding-batch and realtime session JSON respons#97533
vincentkoc merged 1 commit into
openclaw:mainfrom
hugenshen:fix/bound-openai-embedding-realtime-json-responses

Conversation

@hugenshen

Copy link
Copy Markdown
Contributor

What Problem This Solves

Fixes an issue where two OpenAI success-path JSON reads are unbounded, allowing a misbehaving or hostile endpoint to stream an arbitrarily large response body and cause memory pressure or OOM:

  1. extensions/openai/embedding-batch.tsfetchOpenAiBatchStatus polls /batches/:id to track embedding batch progress. The error path was already bounded via readResponseTextLimited, but the success-path parse callback used a raw await res.json() with no size cap. This is the same asymmetric gap fixed for sibling batch surfaces in fix(embedding): bound OpenAI-compatible embedding response reads #96868 (OpenAI-compatible embeddings) and fix(voyage): bound embedding-batch status, error, and non-OK responses #96608 (GitHub Copilot usage).

  2. extensions/openai/realtime-provider-shared.tscreateOpenAIRealtimeSecret fetches ephemeral session tokens for Realtime voice and transcription. The error path used createProviderHttpError (bounded), while the success path fell back to a raw await response.json(). This shared helper is called by both createOpenAIRealtimeClientSecret and createOpenAIRealtimeTranscriptionClientSecret, so both Realtime entry points were affected.

Why This Change Was Made

readProviderJsonResponse from openclaw/plugin-sdk/provider-http wraps readResponseWithLimit with the standard 16 MiB cap and stream cancellation. It is a one-call replacement that makes the success path symmetric with the existing error-path bounds, with no behavior change for well-formed endpoints.

User Impact

No change for normal OpenAI endpoints. Users running memory embedding batches or Realtime voice/transcription against a self-hosted, misconfigured, or compromised baseUrl that returns an oversized success payload will now see a bounded error (Content too large: N bytes (limit: 16777216 bytes)) and early stream cancellation instead of unbounded heap growth.

Evidence

Proof script (node scripts/proof-openai-bound-batch-realtime.mjs, Node v22.22.0):

[case 1] openai.batch-status oversized, ReadableStream, cap=1 MiB
  ok: readResponseWithLimit rejected on oversized batch-status body — true
  ok: bounded error message present (got: Content too large: 2097152 bytes (limit: 1048576 bytes)) — true
  ok: stream was cancelled before all 20 chunks were read (readCount=2) — true
  ok: stream cancel() was called — true

[case 2] negative control — raw response.json() does NOT cancel
  ok: unbounded .json() drained all 20 chunks (readCount=20) — true
  ok: stream NOT cancelled via .cancel() — drained to EOF — true

[case 3] small batch-status body parses cleanly
  ok: small batch-status body parsed into result — true
  ok: result content intact (status=completed present) — true

[case 4] openai.realtime-session oversized, node:http, bytes-on-wire
  ok: readResponseWithLimit rejected realtime-session oversized body — true
  ok: bounded error message present (got: Content too large: 1112544 bytes (limit: 1048576 bytes)) — true

[case 5] small realtime-session body parses cleanly
  ok: small realtime-session body parsed into result — true
  ok: result content intact (batch-ok present) — true

ALL PROOF ASSERTIONS PASSED

Local test suite (node scripts/run-vitest.mjs run extensions/openai/embedding-batch.test.ts extensions/openai/realtime-provider-shared.test.ts, Vitest v4.1.8, Node v22.22.0):

 ✓ extensions/openai/realtime-provider-shared.test.ts > createOpenAIRealtimeClientSecret > returns client secret from a well-formed response
 ✓ extensions/openai/realtime-provider-shared.test.ts > createOpenAIRealtimeClientSecret > bounds oversized success response and cancels the stream
 ✓ extensions/openai/realtime-provider-shared.test.ts > createOpenAIRealtimeClientSecret > throws the provider error label on oversized body
 ✓ extensions/openai/embedding-batch.test.ts > OpenAI embedding batch output > wraps malformed JSONL output
 ✓ extensions/openai/embedding-batch.test.ts > OpenAI embedding batch output > splits provider uploads by serialized JSONL byte cap
 ✓ extensions/openai/embedding-batch.test.ts > OpenAI embedding batch output > adapts OpenAI-compatible upload groups after payload-size rejection
 ✓ extensions/openai/embedding-batch.test.ts > OpenAI embedding batch output > bounds batch status success body via readProviderJsonResponse
 ✓ extensions/openai/embedding-batch.test.ts > OpenAI embedding batch output > bounds batch resource error bodies without using response.text()

 Test Files  2 passed (2)
      Tests  8 passed (8)
   Duration  16.97s

What was not tested: live OpenAI embedding batch API or Realtime API calls.

AI-assisted.

@clawsweeper

clawsweeper Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

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

Summary
The PR replaces two OpenAI success-path JSON reads with the shared bounded provider JSON reader and adds focused Batch status and Realtime session-secret regression tests.

PR surface: Source +4, Tests +164. Total +168 across 4 files.

Reproducibility: Source-level yes. Current main reaches raw res.json() in OpenAI Batch status and raw response.json() in Realtime session-secret success paths; the PR body includes terminal proof for oversized streams and small payloads, but this read-only review did not rerun tests.

Review metrics: 1 noteworthy metric.

  • Provider success JSON caps: 2 added. The PR adds finite success-body rejection points to two OpenAI provider paths where current main buffers successful JSON without a byte cap.

Stored data model
Persistent data-model change detected: serialized state: extensions/openai/realtime-provider-shared.test.ts, unknown-data-model-change: extensions/openai/embedding-batch.test.ts, vector/embedding metadata: extensions/openai/embedding-batch.test.ts, vector/embedding metadata: extensions/openai/embedding-batch.ts. Confirm migration or upgrade compatibility proof before merge.

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] Successful OpenAI Batch status or Realtime session JSON responses above 16 MiB will now fail fast rather than being buffered and parsed, which is an intentional compatibility/availability tradeoff.
  • [P1] The remaining OpenAI response-read sweep is not complete here; OAuth token reads and Batch output file content remain outside this narrow PR and should stay tracked separately if maintainers want full coverage.

Maintainer options:

  1. Land With Shared Cap (recommended)
    If maintainers accept the existing shared 16 MiB provider JSON cap for these small success payloads, this PR is the clean narrow landing path.
  2. Fold Into A Broader Sweep
    Ask for an owner-led OpenAI response-read sweep if OAuth token responses and Batch output file content should be handled before landing this slice.
  3. Defer Only If The Broader PR Revives
    Pause this PR only if fix(openai): bound JSON/text response reads to prevent OOM #96323 becomes proof-positive and clearly safer as the canonical landing path.

Next step before merge

  • No automated repair is needed; maintainers need to accept the compatibility/availability tradeoff of adding shared 16 MiB caps and decide whether to land this narrow slice or fold it into a broader sweep.

Security
Cleared: No supply-chain, workflow, dependency, permission, or secret-handling regression was found; the diff reduces untrusted response buffering risk.

Review details

Best possible solution:

Land this narrow shared-reader change if maintainers accept the 16 MiB success-response cap, and track remaining OpenAI response-read hardening outside this PR.

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

Source-level yes. Current main reaches raw res.json() in OpenAI Batch status and raw response.json() in Realtime session-secret success paths; the PR body includes terminal proof for oversized streams and small payloads, but this read-only review did not rerun tests.

Is this the best way to solve the issue?

Yes for the two targeted JSON paths. Reusing readProviderJsonResponse is the narrow SDK-boundary fix; the maintainer choice is whether to land this slice separately from the broader OpenAI response-read sweep.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 891096926e27.

Label changes

Label justifications:

  • P2: This is focused OpenAI provider availability hardening with limited blast radius and no blocking patch defect.
  • merge-risk: 🚨 compatibility: Successful provider JSON responses above the new cap would fail where current main attempts to buffer and parse them.
  • merge-risk: 🚨 availability: The cap reduces OOM risk but can turn an unexpectedly large legitimate success payload into a runtime failure.
  • 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 (terminal): The PR body includes copied terminal proof for after-fix oversized and small Batch/Realtime JSON behavior, including a negative control and focused test output.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes copied terminal proof for after-fix oversized and small Batch/Realtime JSON behavior, including a negative control and focused test output.
Evidence reviewed

PR surface:

Source +4, Tests +164. Total +168 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 2 7 3 +4
Tests 2 164 0 +164
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 4 171 3 +168

What I checked:

Likely related people:

  • steipete: History shows the OpenAI embedding batch file and Realtime shared helper were introduced or refactored through his commits. (role: feature history owner; confidence: high; commits: 77e6e4cf87f7, 6dfdc92bd4c6; files: extensions/openai/embedding-batch.ts, extensions/openai/realtime-provider-shared.ts)
  • Alix-007: Authored the commit that changed readProviderJsonResponse from raw response.json() to the bounded 16 MiB reader used by this PR. (role: shared bounded-reader contributor; confidence: high; commits: 2592f8a51a4e; files: src/agents/provider-http-errors.ts, src/agents/provider-http-errors.test.ts)
  • vincentkoc: Recently bounded OpenAI batch error bodies and has adjacent provider transport history near this response-read surface. (role: recent OpenAI response hardening contributor; confidence: medium; commits: 5df5aa164052, b0f94a227b3e; files: extensions/openai/embedding-batch.ts, src/agents/provider-http-errors.ts)
  • joshavant: Recent provider HTTP history includes successful provider response read hardening adjacent to the cap-policy decision here. (role: recent shared provider response contributor; confidence: medium; commits: 0a14444924e3; files: src/agents/provider-http-errors.ts, src/plugin-sdk/provider-http.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. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels Jun 28, 2026
@vincentkoc
vincentkoc merged commit 4c477ee into openclaw:main Jun 28, 2026
146 of 158 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 29, 2026
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
Rorqualx pushed a commit to Rorqualx/cortex that referenced this pull request Jul 2, 2026
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: openai merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. 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.

2 participants