Skip to content

fix(google): bound Veo video operation response reads#96605

Merged
sallyom merged 1 commit into
openclaw:mainfrom
Alix-007:fix/bound-google-video
Jun 25, 2026
Merged

fix(google): bound Veo video operation response reads#96605
sallyom merged 1 commit into
openclaw:mainfrom
Alix-007:fix/bound-google-video

Conversation

@Alix-007

@Alix-007 Alix-007 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

The Google Veo video REST operation read in extensions/google/video-generation-provider.ts
(requestGoogleVideoJson) buffers the entire external response via an unbounded
await response.text() before checking response.ok. The Veo create/poll response is untrusted
provider content: a hostile or misconfigured endpoint can stream an arbitrarily large,
Content-Length-less body that is fully read into memory before parsing — a memory-pressure / hang
(OOM) vector on the video-generation path.

Scope is the video-generation operation JSON only. This is distinct from #96324, which covers
Google embedding/image/media/oauth/speech and does not touch video-generation-provider.ts.

Changes

  • Route the Veo REST operation read through the shared bounded reader readResponseWithLimit
    (re-exported via openclaw/plugin-sdk/response-limit-runtime, from @openclaw/media-core)
    instead of await response.text(), capped at a new GOOGLE_VIDEO_OPERATION_RESPONSE_MAX_BYTES
    (16 MiB, aligned with merged provider bound-stream PRs fix(agents): bound provider JSON response reads #95218 / fix(ollama): bound model-discovery JSON response reads #96027 / fix(exa): bound untrusted search JSON response reads #96038). The result is
    TextDecoder().decoded before the existing response.ok / HTTP-detail / JSON.parse flow.
  • On overflow the reader cancels the underlying stream and throws a bounded error
    (Google video operation response exceeds 16777216 bytes). The response.ok error-detail path
    and the success JSON.parse shape are otherwise unchanged.
  • Both create and poll share requestGoogleVideoJson (generateGoogleVideoViaRest calls it for
    the :predictLongRunning create POST and for each GET <operation name> poll), so the single
    bounded read covers both verbs.
  • No new abstraction — reuses the media-core bounded reader already used across the codebase.

Real behavior proof

  • Behavior addressed: The unbounded await response.text() on the Veo REST operation read let
    an untrusted endpoint stream an unbounded, Content-Length-less JSON body fully into memory. The
    read must instead stop at the 16 MiB cap, cancel the stream, and throw a bounded error — while
    small valid operation JSON still parses unchanged.
  • Real environment tested: A real local node:http server (createServer) bound to
    127.0.0.1, streaming a JSON operation body in 64 KiB chunks with no Content-Length header
    (would total ~64 MiB, 4× the 16 MiB cap), reached over the real global fetch + real TCP
    socket
    (not an in-process ReadableStream fixture, not a mocked fetch). The proof drives the
    exact post-fix requestGoogleVideoJson read bodyreadResponseWithLimit(response, GOOGLE_VIDEO_OPERATION_RESPONSE_MAX_BYTES, { onOverflow })TextDecoder().decode
    response.ok branch → JSON.parse — against that real socket, for both the create POST and the
    poll GET
    verbs that share the helper. readResponseWithLimit is the real @openclaw/media-core
    function the changed line calls. Node v22.22.0.
  • Exact steps: node --import tsx proof.mts — start the real server → drive the shared read path
    against POST /create-huge and GET /poll-huge (oversized, no Content-Length) and assert each
    throws the bounded error + the server observed the socket aborted with bytes-on-wire ≪ the full
    ~64 MiB → run a negative control (the OLD unbounded read of the same body, measuring it buffers
    past the cap) → drive the shared read path against small valid create/poll operation JSON and
    assert they still parse.
  • Evidence after fix: For both create and poll, readResponseWithLimit threw
    Google video operation response exceeds 16777216 bytes; the server observed the socket aborted
    after ~17–18 MiB (≪ the ~64 MiB it would have sent), confirming the stream was cancelled, not
    drained. Small create/poll operation JSON parsed back into intact operation objects
    (name/done, and the polled done:true result with one generated sample).
  • Observed result (all 7 checks PASS):
    • create POST shared path: overflow throws bounded error → threw, msg exceeds 16777216 bytes
    • create POST shared path: stream cancelled near 16MiB → aborted=true, bytesSent=18284544 (< 33554432)
    • poll GET shared path: overflow throws bounded error → threw, msg exceeds 16777216 bytes
    • poll GET shared path: stream cancelled near 16MiB → aborted=true, bytesSent=17825792 (< 33554432)
    • negative control: OLD unbounded read buffers PAST the 16MiB cap → buffered 16799559 bytes (> cap)
      — proves the cap is load-bearing (without it the body keeps accumulating past 16 MiB).
    • happy path: small create operation JSON still parsedname=operations/veo-abc done=false
    • happy path: small poll operation JSON still parseddone=true samples=1
  • What was not tested: Did not hit the live generativelanguage.googleapis.com Veo endpoint
    (no key / would not reproduce a hostile oversized body); the untrusted-body behavior is fully
    reproduced with a local streaming server over the same transport. The proof did not drive the read
    to actual OOM — it measures bytes-on-wire + early socket abort, which is the load-bearing signal.
    The proof script is not committed.

Evidence

Real node:http terminal proof (node --import tsx proof.mts):

[proof] real node:http server on http://127.0.0.1:41913, cap=16777216 bytes, would-stream≈67108864 bytes

PASS  create POST shared path: overflow throws bounded error :: threw=true msg="Google video operation response exceeds 16777216 bytes"
PASS  create POST shared path: stream cancelled near 16MiB (socket aborted, bytes ≪ 64MiB) :: aborted=true bytesSent=18284544 (< 33554432)
PASS  poll GET shared path: overflow throws bounded error :: threw=true msg="Google video operation response exceeds 16777216 bytes"
PASS  poll GET shared path: stream cancelled near 16MiB (socket aborted, bytes ≪ 64MiB) :: aborted=true bytesSent=17825792 (< 33554432)
PASS  negative control: OLD unbounded read buffers PAST the 16MiB cap (cap is load-bearing) :: buffered=16799559 bytes (> 16777216)
PASS  happy path: small create operation JSON still parsed (name, done:false) :: name=operations/veo-abc done=false
PASS  happy path: small poll operation JSON still parsed (done:true, 1 generated sample) :: done=true samples=1

[proof] 7 PASS / 0 FAIL
[proof] ALL PASS

In-repo Vitest suite for this provider (node scripts/run-vitest.mjs extensions/google/video-generation-provider.test.ts --run) — includes a streaming-fixture
regression test asserting the oversized read stops before consuming all chunks and cancels the
stream:

 Test Files  1 passed (1)
      Tests  16 passed (16)

oxlint extensions/google/video-generation-provider.ts → exit 0, clean.

This is the Google Veo video-operation counterpart to the #95103 / #95108 response-limit campaign,
applying the same @openclaw/media-core bounded reader to the remaining unbounded provider read.

Label: security

AI-assisted.

@clawsweeper

clawsweeper Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 24, 2026, 11:21 PM ET / 03:21 UTC.

Summary
The branch routes Google Veo REST operation JSON reads through a 16 MiB bounded response reader and adds a streaming regression test for the REST fallback path.

PR surface: Source +9, Tests +60. Total +69 across 2 files.

Reproducibility: yes. Current main has an unbounded response.text() in the shared Veo REST operation helper, and the PR body gives a concrete local TCP/fetch proof path for oversized create and poll operation bodies.

Review metrics: 1 noteworthy metric.

  • Runtime response limit: 1 added. The PR introduces one fixed provider-runtime response ceiling that can reject operation JSON bodies current main would fully buffer.

Root-cause cluster
Relationship: canonical
Canonical: #96605
Summary: This PR is the canonical item for bounding the Google Veo operation-response read; related items are adjacent bounded-response hardening or a broader Google JSON sweep that does not touch this file.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

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:

  • [P2] Maintainer should accept the 16 MiB Veo operation JSON ceiling or request a different cap before merge.

Risk before merge

  • [P1] A legitimate Google Veo operation JSON response larger than 16 MiB would now fail with a bounded error where current main would fully buffer and try to parse it.

Maintainer options:

  1. Accept the 16 MiB operation cap (recommended)
    Maintainers can accept 16 MiB as the intended Veo operation-metadata ceiling, matching the recent bounded provider response hardening pattern.
  2. Tune the cap before merge
    If legitimate Veo operation metadata can exceed 16 MiB, adjust the constant and keep the bounded-stream regression and real proof aligned with the chosen limit.

Next step before merge

  • No automated repair is queued because the remaining action is maintainer acceptance or tuning of the fixed Veo operation response cap.

Security
Cleared: The diff adds no dependencies, scripts, workflow permissions, credential handling, or package-resolution changes, and it reduces an external-response memory-pressure surface.

Review details

Best possible solution:

Land the bounded read in the Google plugin after maintainers accept or tune the 16 MiB Veo operation-metadata ceiling.

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

Yes. Current main has an unbounded response.text() in the shared Veo REST operation helper, and the PR body gives a concrete local TCP/fetch proof path for oversized create and poll operation bodies.

Is this the best way to solve the issue?

Yes, with a maintainer cap decision remaining. Reusing the existing plugin SDK bounded reader inside the Google plugin helper is the narrowest owner-boundary fix; the broader Google JSON sweep remains separate at #96324.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 643410c1f3c0.

Label changes

Label justifications:

  • P2: The PR addresses a provider-specific memory-pressure hardening bug with limited Google video fallback blast radius.
  • merge-risk: 🚨 compatibility: The fixed 16 MiB cap can fail Google Veo operation responses that current main would fully buffer and parse.
  • 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 after-fix terminal proof from a real local node:http server over TCP/fetch showing bounded create and poll failures, socket cancellation near the cap, and small JSON success.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal proof from a real local node:http server over TCP/fetch showing bounded create and poll failures, socket cancellation near the cap, and small JSON success.
Evidence reviewed

PR surface:

Source +9, Tests +60. Total +69 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 10 1 +9
Tests 1 60 0 +60
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 70 1 +69

What I checked:

Likely related people:

  • steipete: Git history shows Peter Steinberger introduced video provider support and made follow-up Google video provider changes around video sizing and live video coverage. (role: feature introducer and primary area contributor; confidence: high; commits: 932194b7d58c, a88c6f0fe713, 79a84f070de7; files: extensions/google/video-generation-provider.ts, extensions/google/video-generation-provider.test.ts)
  • shakkernerd: Local blame for the current requestGoogleVideoJson helper and the current Google video provider files points to commit 9d82906. (role: recent current-main area contributor; confidence: medium; commits: 9d82906f792b; files: extensions/google/video-generation-provider.ts, extensions/google/video-generation-provider.test.ts)
  • velvet-shark: A recent Veo request-shape fix changed the same Google video provider files, making this a relevant adjacent routing signal. (role: recent Veo compatibility contributor; confidence: medium; commits: f2a4a5ac21d3; files: extensions/google/video-generation-provider.ts, extensions/google/video-generation-provider.test.ts)
  • Alix-007: Beyond this PR, this contributor authored merged bounded-response PRs for provider JSON and plugin discovery/search reads that this patch follows. (role: adjacent response-limit contributor; confidence: medium; commits: 2592f8a51a4e, d1c2934d0d17, 605aede38c10; files: src/agents/provider-http-errors.ts, extensions/ollama/src/provider-models.ts, extensions/exa/src/exa-web-search-provider.runtime.ts)
  • joshavant: Recent main history added the shared provider JSON response cap that this PR mirrors for the Google Veo operation helper. (role: recent response-limit maintainer; confidence: medium; commits: 0a14444924e3; files: src/agents/provider-http-errors.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 rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. 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
@Alix-007

Copy link
Copy Markdown
Contributor Author

@clawsweeper review

@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.

@Alix-007
Alix-007 force-pushed the fix/bound-google-video branch from 1aae5ff to e9f508f Compare June 25, 2026 02:35
@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. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 25, 2026
@sallyom

sallyom commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Maintainer local review: merge-ready provided green CI, reviewed as a batch:

Autoreview is clean, the 16 MiB cap is acceptable for these provider JSON/status/error/usage bodies, and the changes follow the established shared bounded-reader pattern. This is the best narrow fix for these call sites: no new SDK/API/config surface, no upgrade/backward-compatibility concern beyond the intended oversized-body failure mode, and no expected breaking behavior for normal provider responses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: google 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