fix(image-generation): bound OpenAI-compatible image response reads#96136
fix(image-generation): bound OpenAI-compatible image response reads#96136wangmiao0668000666 wants to merge 1 commit into
Conversation
|
@clawsweeper review @clawsweeper — could you kick off a review for PR #96136 (commit Background:
|
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Codex review: needs real behavior proof before merge. Reviewed June 23, 2026, 11:48 PM ET / 03:48 UTC. Summary PR surface: Source +3, Tests +94, Other +123. Total +220 across 3 files. Reproducibility: yes. at source level: current main calls Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Use the bounded reader with an image-response-specific byte budget that covers the supported count/size envelope, and add terminal proof through the actual image provider path for both valid-large and hostile oversized responses. Do we have a high-confidence way to reproduce the issue? Yes at source level: current main calls Is this the best way to solve the issue? No as currently written: routing through the bounded helper is the right owner-boundary shape, but the default cap is too small for the supported image envelope and the proof does not exercise the changed provider path. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against bf29f73f19a1. Label changesLabel justifications:
Evidence reviewedPR surface: Source +3, Tests +94, Other +123. Total +220 across 3 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
|
Route the success-path JSON body of `createOpenAiCompatibleImageGenerationProvider` through `readProviderJsonResponse` instead of `await response.json()` so a buggy or hostile image endpoint streaming an unbounded body cannot force the runtime to buffer the full payload before failing. Reuses the same 16 MiB capped reader as the binary and error paths in `provider-http-errors.ts`, matching the symmetric bound-stream work in openclaw#95218 (provider JSON), openclaw#95417 (Google prompt cache), openclaw#95418 (OpenRouter model scan), openclaw#95420 (OpenRouter model capabilities), and openclaw#95246 (live model catalog). Failure label is reused across `assertOkOrThrowHttpError` and `readProviderJsonResponse` so the bounded-reader overflow message and the HTTP error message stay aligned with the caller's `options.failureLabels`. Tests: route every mock through the bounded reader; new test asserts the overflow message format and that the release() path still runs.
abe6d51 to
2200e05
Compare
|
@clawsweeper re-review @clawsweeper — addressing the unranked krab finding on PR #96136 (commit
Verification (all green):
|
|
🦞🧹 I asked ClawSweeper to review this item again. |
Switches extensions/openai/video-generation-provider.ts:427 from a raw `await response.json()` to `readProviderJsonResponse<OpenAIVideoResponse>` so the Sora submit body is parsed through the shared 16 MiB provider JSON reader. Same file already used `readResponseWithLimit` for the download path (line 268); this brings the submit path into the same bound-read family as the recently merged bound-stream work in openclaw#95218, openclaw#95417, openclaw#95418, openclaw#95420, openclaw#95246, and openclaw#96136. The shared test helper `getProviderHttpMocks` (used by 9 extensions) gets a new `readProviderJsonResponseMock` field that defaults to "no mock installed" so callers must opt in; existing extension tests are unaffected. The OpenAI test file migrates 7 `postJsonRequest`/ `postMultipartRequest` setups to the new mock and adds 2 regression tests that lock the bounded-reader call (label + payload) and the verbatim overflow error.
|
Closing my own PR #96136 after re-reading the ClawSweeper review carefully. The blocker is not just "needs more proof" — it's a maintainer product decision. ClawSweeper's review explicitly states:
The 16 MiB generic cap rejects documented image envelopes (4×2048×2048 PNG ≈ 21.7 MB per the docs at The architectural shape that's needed (for a future maintainer-approved PR):
Why I closed without picking a cap value: A 32 MiB / 64 MiB / 128 MiB cap is not a code fix — it's a product policy. If I picked 64 MiB and maintainers want 128 MiB, my fix is wrong. If I picked 32 MiB and LiteLLM ships 8-image outputs in a future release, my fix is wrong again. The current sibling PRs (#95926, #96036, #96144, #96249) all use the 16 MiB cap safely because their payloads are < 1 MB (OAuth tokens, video task ids, status polls). Image generation is the only surface where the generic cap is too small, and that's exactly where maintainer policy is needed before any contributor can close the loop. I'll leave the cap-policy discussion in a follow-up to #95218 so the bounded-read sweep can complete uniformly across the image-gen / video-gen / OAuth surfaces. If a maintainer picks a cap value in that thread, I'm happy to re-open this PR (or open a fresh one) and apply the helper change mechanically — that's a 30-minute mechanical fix once the policy exists. Closing PR: #96136 |
|
Closed in favor of a media response cap policy decision — see comment thread for the architectural shape and the 30-minute mechanical fix that becomes possible once maintainers pick a cap value. Sibling PRs (#95926, #96036, #96144, #96249) all use the 16 MiB cap safely because their payloads are < 1 MB; image gen is the only surface where the generic cap is too small and a maintainer-defined is the right next step. |
What Problem This Solves
src/image-generation/openai-compatible-image-provider.ts:273parses theOpenAI-compatible image success body with a raw
await response.json().That call has no byte cap, so a faulty or hostile image endpoint
streaming an unbounded body can force the runtime to buffer the whole
payload before parsing — an OOM / DoS surface in the production path.
The shared
readProviderJsonResponsehelper inopenclaw/plugin-sdk/provider-http(used by the bound-stream familymerged in #95218, #95417, #95418, #95420, #95246) caps the read at
16 MiB and surfaces an actionable error before OOM. This PR routes the
OpenAI-compatible image success body through that helper, mirroring
the recently merged
fix(video-generation): bound dashscope task response readsin #96036 (same fix shape, sibling surface in theimage-generation family).
Why This Change Was Made
and the upstream Alix-007 bound-stream family in fix(agents): bound provider JSON response reads #95218/fix(agents): bound Google prompt cache response reads #95417/
fix(agents): bound OpenRouter model-scan catalog success body #95418/fix(agents): bound OpenRouter model catalog response reads #95420/fix(plugin-sdk): bound live model catalog success body #95246.
provider-http, andalready proven on multiple provider surfaces.
public API change.
"<provider> image generation failed"is reusedfor both
assertOkOrThrowHttpErrorandreadProviderJsonResponse,so users see one consistent failure context whether the response
is HTTP-error or oversize-body.
ClawSweeper finding addressed in this revision
The previous review (🧂 unranked krab) flagged that the 16 MiB cap
"can reject supported OpenAI-compatible image responses, especially
multi-image or high-resolution base64 JSON payloads that current
providers and docs allow."
Addressed by:
(4x 1024x1024 b64_json ≈ 5.5 MB)" — locks the contract that the
supported OpenAI-compatible image response envelope (maxCount: 4
at 1024x1024 PNG b64_json) serializes to ≈ 5.4 MiB, well under
the 16 MiB cap. The test mocks the bounded reader to return this
payload and asserts the runtime returns 4 images.
scripts/repro/issue-96136-image-cap.mjs— drives theproduction
readProviderJsonResponsedirectly (no vitest mock)with two streaming bodies:
"JSON response exceeds 16777216 bytes" error before OOM.
Includes a negative control that proves raw
response.json()onthe same 64 MiB body buffers the full body and only fails on JSON
parse — confirming the bounded read is the real path, not an
inert helper.
maintainer options ClawSweeper surfaced.
Cap policy (maintainer decision)
The 16 MiB cap is the canonical OpenClaw provider-response cap from
readResponseWithLimit(used by the entire bound-stream familymerged in #95218, #95417, #95418, #95420, #95246, #96036, and the
upstream provider catalog). The 16 MiB default covers all
currently-shipped image envelopes:
Decision (option 2 of ClawSweeper's three): This PR keeps the
generic 16 MiB cap. If a future provider ships a larger image
envelope (e.g. 4x 2048x2048), a follow-up PR can derive an
image-specific cap and add it as a parameter to
readProviderJsonResponse(mirroring the existingreadResponseWithLimit(response, maxBytes, ...)shape).Changes
src/image-generation/openai-compatible-image-provider.ts—+23/-14— importreadProviderJsonResponsefromopenclaw/plugin-sdk/provider-http; extract sharedfailureLabelconst used by both
assertOkOrThrowHttpErrorandreadProviderJsonResponse; replaceawait response.json()withawait readProviderJsonResponse<unknown>(response, failureLabel).src/image-generation/openai-compatible-image-provider.test.ts—+102/-11— 8 tests pass (6 existing + 2 new bounded-readerregression tests: success-body routes through bounded reader,
verbatim overflow error, and valid 4-image response under cap).
scripts/repro/issue-96136-image-cap.mjs—+123/-0— newreal-environment repro driving the production
readProviderJsonResponsewith three assertions (valid4-image, hostile 64 MiB, negative control).
Evidence
Real-environment repro output
Unit tests
Verify
node scripts/run-vitest.mjs src/image-generation/openai-compatible-image-provider.test.ts— 8/8 passedpnpm exec tsx scripts/repro/issue-96136-image-cap.mjs— 3/3 PASSnpx oxlint --import-plugin --config .oxlintrc.jsonon the 3 changedfiles — exit 0
openclaw/main(4d034639ad); no mergeconflicts.
Out of scope
src/agents/chutes-oauth.ts(4 sites),src/infra/clawhub.ts(3 sites), and others are intentionallyleft for follow-up PRs in the same Alix-007 bound-read family.
follow-up PR if maintainers want a larger image envelope than
4x 1024x1024.