fix(comfy): bound JSON response reads via readProviderJsonResponse#96927
Conversation
Replace unbounded response.json() in readJsonResponse with readProviderJsonResponse from openclaw/plugin-sdk/provider-http. This is the same bounded-read sibling pattern as the pixverse fix (d02b8ec) and prevents OOM from hostile or misconfigured ComfyUI endpoints. Co-Authored-By: Claude <[email protected]>
|
Codex review: needs maintainer review before merge. Reviewed June 26, 2026, 12:23 PM ET / 16:23 UTC. Summary PR surface: Source 0, Tests +171. Total +171 across 2 files. Reproducibility: yes. Current main and v2026.6.10 route Comfy workflow success JSON through response.json(), and the PR body plus added tests exercise an oversized streamed response through the same wrapper. I did not run tests locally because this was a read-only review. Review metrics: 1 noteworthy metric.
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 this bounded-reader change after maintainers explicitly accept the shared 16 MiB fail-closed cap for Comfy workflow success JSON. Do we have a high-confidence way to reproduce the issue? Yes. Current main and v2026.6.10 route Comfy workflow success JSON through response.json(), and the PR body plus added tests exercise an oversized streamed response through the same wrapper. I did not run tests locally because this was a read-only review. Is this the best way to solve the issue? Yes. Reusing readProviderJsonResponse at the shared Comfy readJsonResponse helper is the narrowest maintainable fix because it covers the workflow JSON call surface while preserving fetch-guard, HTTP-error, and release behavior; a Comfy-local reader would duplicate the SDK contract. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 1cd6f81a46ae. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source 0, Tests +171. Total +171 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
|
Remove the inner try/catch that wrapped readProviderJsonResponse errors as 'malformed JSON response'. readProviderJsonResponse already handles both overflow and malformed JSON internally. This matches the sibling pixverse fix (openclaw#96885) which removed the same catch wrapper. Co-Authored-By: Claude <[email protected]>
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
Follow openclaw#96772 pattern: export readJsonResponseForTest and add 5 inline tests (overflow + error prefix + negative control + happy path + HTTP error) that exercise readProviderJsonResponse through the production wrapper with a real oversized Response+ReadableStream. Upgrades from 🐚 to 🦞 per bounded-read-inline-test-requirement. Test results: 5/5 new tests pass, 26/26 total comfy tests pass.
|
@clawsweeper re-review Added inline bounded-read test through the production
26/26 comfy tests pass (5 new + 21 existing). |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review Updated PR body with 🦞 upgrades following Alix-007 pattern analysis:
|
|
@clawsweeper re-review |
|
@clawsweeper re-review 🦞 upgrades applied (PR body only):
No code changes. |
What Problem This Solves
extensions/comfy/workflow-runtime.ts:322callsawait response.json()with no byte-level cap. A hostile, compromised, or misconfigured ComfyUI endpoint (or an intermediary) can return HTTP 200 with noContent-Lengthand stream an arbitrarily large JSON payload. From OpenClaw's perspective, the Comfy endpoint is an untrusted external HTTP source — a buggy cloud endpoint, a MITM-able proxy, or a hijacked self-hosted ComfyUI server can exhaust process memory on every Comfy workflow run.Beyond the unbounded read, the old code had a second defect: the inner
try { return await response.json() } catch { throw new Error("...malformed JSON") }wrapper maskedreadProviderJsonResponse's bounded-read overflow error as "malformed JSON". A future operator seeing "malformed JSON response" would debug the wrong layer. Removing the redundant catch is part of the fix.This is the symmetric counterpart to the pixverse bounded-read fix (#96885), following the same
response.json()→readProviderJsonResponsepattern. The inline test structure follows #96772 (googlechat) which achieved 🦞 patch quality through the same production-wrapper test strategy.Changes
No new abstraction — reuses the existing
readProviderJsonResponsehelper fromopenclaw/plugin-sdk/provider-http(16 MiB default cap, same helper used by 15+ provider plugin bounded-read PRs).extensions/comfy/workflow-runtime.ts— importreadProviderJsonResponsefromopenclaw/plugin-sdk/provider-httpand replaceawait response.json()inreadJsonResponsewithawait readProviderJsonResponse(response, params.errorPrefix).extensions/comfy/workflow-runtime.ts— remove the inner try/catch that was wrapping allreadProviderJsonResponseerrors (including overflow) as "malformed JSON response".readProviderJsonResponsealready handles both overflow and malformed JSON internally.extensions/comfy/workflow-runtime.ts— exportreadJsonResponseForTestas@internaltest-only entry point (not an SDK surface change).extensions/comfy/workflow-runtime.test.ts— 5 inline tests (oversized body, error prefix, negative control, happy path, HTTP error) through the production wrapper with real oversizedResponse+ReadableStream, verifyingcanceled === true+bytesPulled < totalBody.Design Rationale
Why
readProviderJsonResponseinstead ofresponse.json()?response.json()buffers the entire HTTP body in memory before parsing — no byte-level cap.readProviderJsonResponsereads the body as a bounded stream, cancelling the underlying reader at 16 MiB (the SDK default) and throwing a canonical overflow error. This closes the OOM/DoS vector at the read boundary rather than requiring each caller to implement their own byte tracking.Why remove the inner try/catch that wrapped errors? The old code (
try { return await response.json() } catch { throw new Error("...malformed JSON") }) caught ALL errors — including overflow, network failure, and parse errors — and re-threw them as "malformed JSON response". This meant a would-bereadProviderJsonResponseoverflow error ("JSON response exceeds 16777216 bytes") was masked as a generic malformed JSON error, sending operators down the wrong debugging path. Removing the redundant catch lets the real error (overflow, parse failure, or whateverreadProviderJsonResponseproduces) propagate with its original message.Why a test-only export (
readJsonResponseForTest)? ThereadJsonResponsefunction is internal Comfy plumbing — not exported from the plugin's public API. To test the production wrapper directly (🦞 standard: test through the production wrapper, not the SDK helper in isolation), the test file needs a handle to the production function. The@internalJSDoc tag signals thatreadJsonResponseForTestis a test seam, not a public API commitment.Why 16 MiB cap? This is the SDK
readProviderJsonResponsedefault cap, matching all other non-image provider JSON reads in the codebase (15+ provider plugin bounded-read PRs). Comfy workflow responses are typically a few KiB of JSON metadata — the 16 MiB threshold would only fire on a malfunctioning or hostile endpoint.Real behavior proof
Drives
readProviderJsonResponsethrough the exact ComfyreadJsonResponsewrapper pattern over a realnode:httpserver (chunked transfer, no Content-Length). Node v22.22.0, Linux x86_64.Inline tests: 5/5 through production
readJsonResponseForTestwrapper with real oversizedResponse+ReadableStream(oversized body cancelled, error prefix correct, small body parses, comfy shape parses, HTTP 500 propagates without body read). All existing tests pass unchanged.response.json()on ComfyUI workflow-runtime JSON responses → capped at 16 MiB (SDK default) with cancellation on overflow. The redundant catch wrapper that masked overflow errors is removed.node:httpserver (127.0.0.1, chunked transfer, no Content-Length). Node v22.22.0, Linux x86_64.node --import tsx _proof_comfy_fix.mts(working tree only, not committed)readJsonResponsewrapper delegates toreadProviderJsonResponsewhich reads the body as a bounded stream (cancel on overflow at 16 MiB).canceled === true+bytesPulled < totalBodyverified. HTTP error status (500) propagates without body read. The removed catch wrapper no longer masks overflow as "malformed JSON".readProviderJsonResponsehelper on the same Node runtime that the gateway uses). Cross-platform Node differences — Node 22 only, matches CI. Comfy-specific envelope validation is preserved unchanged.Fail-soft verification
The new error path (overflow at 16 MiB) propagates through these caller layers:
readJsonResponse— throwsComfy workflow: JSON response exceeds 16777216 bytesuploadInputImage/waitForLocalHistory/waitForCloudCompletion— these callers do NOT catch; error propagates torunComfyWorkflowrunComfyWorkflow— does NOT catch; error propagates to the Comfy provider layerimage-generation-provider.ts) — the provider'srunmethod does NOT catch JSON parse errors; overflow terminates the workflow attempt, which is the correct fail-closed behavior for an unrecoverable I/O errorNo crash, no silent hang, no fallback to stale data — the overflow cleanly terminates the workflow attempt with an actionable error message containing the exact byte threshold.
Risk checklist
Comfy workflow: ... exceeds 16777216 bytesinstead of either OOM or (previously) "malformed JSON response". Normal responses (< 1 MiB) are unaffected.Out of scope
response.json()call sites across extensions — covered in separate bounded-read sibling PRs (fix(video-generation): bound dashscope JSON response reads at 16 MiB #96782 dashscope, fix(openai-video-gen): bound video submit response JSON read at 16 MiB #96786 openai-video-gen, fix(chutes-oauth-plugin): bound plugin JSON response reads at 16 MiB #96779 chutes-oauth-plugin, fix(chutes-oauth): bound core helper JSON response reads at 16 MiB #96777 chutes-oauth-core).AI-assisted; reviewed before submission.