Skip to content

fix(clickclack): bound REST success JSON response reads#96970

Merged
vincentkoc merged 2 commits into
openclaw:mainfrom
mushuiyu886:fix/followup-96505
Jun 27, 2026
Merged

fix(clickclack): bound REST success JSON response reads#96970
vincentkoc merged 2 commits into
openclaw:mainfrom
mushuiyu886:fix/followup-96505

Conversation

@mushuiyu886

Copy link
Copy Markdown
Contributor

Origin / follow-up

Follow-up to #96505. That PR moved an OpenRouter video-catalog success JSON read from raw response.json() to the shared bounded provider JSON reader. This patch applies the same guardrail to the ClickClack REST client boundary, where successful API responses were still parsed with raw response.json().

Summary

  • Route ClickClack REST success JSON responses through readProviderJsonResponse.
  • Keep existing bounded error-body handling with readResponseTextLimited unchanged.
  • Add a loopback HTTP regression that streams an oversized success response and verifies the client closes the stream before the full body is sent.

Maintainer-ready fields

  • Behavior or issue addressed: ClickClack REST success responses crossed an external HTTP boundary through raw response.json() instead of OpenClaw's shared bounded provider JSON reader.
  • Fix classification: Root cause fix
  • Root cause: Root cause source: the ClickClack REST response-body contract was split because unsuccessful responses used the bounded shared reader, while the canonical success-response helper still used raw await response.json() for every 2xx JSON response from the HTTP boundary.
  • Why this is root-cause fix: All ClickClack REST success methods flow through the same request<T>() helper, so changing that helper restores the response-size invariant at the boundary instead of patching individual call sites.
  • Real environment tested: Local TCP loopback HTTP server plus the real ClickClack client and native fetch stream handling; no mocked Response object was used for the overflow/close proof.
  • Exact steps or command run after this patch: Rebased onto current origin/main, ran the extensions Vitest shard, ran pnpm check:test-types, and ran the standalone ClickClack loopback proof script.
  • Evidence after fix: The loopback server recorded error=ClickClack response: JSON response exceeds 16777216 bytes, exceeded_cap=true, and closed_before_full_response=true.
  • Observed result after fix: The client rejected at the shared provider JSON cap and the server observed close at 16,908,288 bytes, before the planned 18 MiB response completed.
  • What was not tested: ClickClack setup flows, plugin configuration, message delivery, and hosted business semantics remain outside this patch; the changed contract is response-body stream handling at the HTTP boundary.
  • Why it matters / User impact: Oversized ClickClack success responses now fail deterministically and close early instead of being fully materialized by native JSON parsing.
  • What did NOT change: Endpoint paths, request headers, auth behavior, websocket behavior, response shapes, and the existing 8 KiB error-body limit are unchanged.
  • Architecture / source-of-truth check: The canonical source-of-truth for provider response-size enforcement is openclaw/plugin-sdk/provider-http; this change moves ClickClack's success JSON read onto that boundary before downstream typed response projection, matching the same shared reader pattern used by fix(openrouter): bound video catalog JSON reads #96505.
  • Target test file: extensions/clickclack/src/http-client.test.ts.
  • Scenario locked in: A loopback HTTP server streams an 18 MiB 200 JSON response; the real client rejects at 16 MiB and the server observes an early close.
  • Why this is the smallest reliable guardrail: One shared helper return path covers every ClickClack REST success read without touching endpoint-specific behavior.
  • Risk labels considered: merge-risk: low, behavior-change: oversized-provider-response, and auth-provider as a detected false-positive risk surface from editing the auth-bearing client file.
  • Risk explanation: The only behavior change is for successful ClickClack JSON bodies over the shared provider cap; auth headers, URL construction, request bodies, response schemas, and websocket behavior are not changed.
  • Why acceptable: Normal ClickClack REST payloads should be far below 16 MiB, and failing oversized provider responses early matches OpenClaw's existing provider response-body policy.
  • Maintainer-ready confidence: High; the diff is two files, the production change is localized to the shared REST success helper, and fresh verification is bound to the rebased commit.
  • Patch quality notes: Focused production diff, no repository evidence files, no unrelated cleanup, and regression coverage beside the existing bounded error-response test.

What Problem This Solves

ClickClack REST responses come from an external service boundary. Before this patch, successful responses used native response.json(), so an oversized or never-ending success body could be fully materialized before failing. The shared bounded JSON reader now enforces the existing 16 MiB provider JSON cap and cancels the stream once the cap is exceeded.

Root Cause

The ClickClack client already bounded error response text through readResponseTextLimited, but the shared REST success helper still returned await response.json(). Every successful ClickClack REST method flows through that helper, so the success path bypassed OpenClaw's provider response-size guardrail.

Why it matters / User impact

An oversized ClickClack success response can otherwise consume memory through native JSON parsing before any ClickClack-specific shape validation runs. With this change, the client fails at the provider JSON cap with a deterministic overflow error and closes the response stream early.

What did NOT change

  • No ClickClack endpoint paths, request headers, auth behavior, websocket behavior, or response shapes changed.
  • Error responses still use the existing 8 KiB text limit and error message format.
  • No new ClickClack-specific size constant or parser was introduced.

Architecture / source-of-truth check

OpenClaw already has a shared provider response-body source of truth in openclaw/plugin-sdk/provider-http. ClickClack already used that module for bounded error text; this patch extends the same boundary helper to successful REST JSON responses instead of adding another local implementation.

Target test file

extensions/clickclack/src/http-client.test.ts

Scenario locked in

The regression starts a local HTTP server, points the real ClickClack client at it, and streams an 18 MiB success JSON response. The client rejects at the shared 16 MiB cap and the server observes the connection closing before the full response is sent.

Evidence

RED observation against the raw response.json() implementation:

AssertionError: expected [Function] to throw error including
'ClickClack response: JSON response exceeds 16777216 bytes'
but got 'Unexpected end of JSON input'

Loopback proof after the fix:

error=ClickClack response: JSON response exceeds 16777216 bytes
closed_bytes=16908288
exceeded_cap=true
closed_before_full_response=true

Real behavior proof

This proof uses a local TCP loopback HTTP server and the real ClickClack client, not a mocked Response. The server attempts to stream 18 MiB; the client rejects once the shared 16 MiB cap is exceeded, and the server's close event fires before the full body is sent.

error=ClickClack response: JSON response exceeds 16777216 bytes
closed_bytes=16908288
exceeded_cap=true
closed_before_full_response=true

Verification

Full extensions Vitest shard after rebasing onto current origin/main:

Test Files  208 passed (208)
Tests  2703 passed (2703)
[test] passed 1 Vitest shard in 333.81s

Test typecheck:

$ pnpm tsgo:test
$ pnpm tsgo:core:test && pnpm tsgo:extensions:test
$ node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core-test.tsbuildinfo
$ node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo

Regression Test Plan

  • Exercise the real ClickClack client against a loopback HTTP server returning a 200 response with an oversized success JSON body.
  • Assert the client rejects with ClickClack response: JSON response exceeds 16777216 bytes.
  • Assert the server observes the stream closing after the cap is exceeded and before the full 18 MiB response is sent.
  • Keep the existing error-body limit regression in place to verify the unsuccessful response path still uses readResponseTextLimited.

Why this is the smallest reliable guardrail

The ClickClack client funnels all REST success reads through one request<T>() helper. Changing that one return path covers me, workspace/channel/message reads, direct-message creation, thread replies, and realtime event polling without touching call-site behavior.

Merge risk

  • Detected category: auth-provider, because the changed file is the ClickClack auth-bearing REST client.
  • Compatibility: Existing auth headers, request URL construction, request bodies, websocket setup, and response shapes are unchanged.
  • Related open PR scan: Fresh Alix-007 open PR comparison found no extensions/clickclack overlap; public open-PR text search for clickclack did not show a competing bounded ClickClack REST response-body PR.
  • Out-of-scope boundary: This PR does not change ClickClack account setup, plugin configuration, schemas, message delivery, channel routing, or live service semantics.

Risk labels considered

  • merge-risk: low
  • behavior-change: oversized-provider-response
  • auth-provider detected and explained above

Risk explanation

The only intentional behavior change is for successful ClickClack JSON bodies over the shared provider cap: they now fail early with a bounded-reader error instead of continuing through raw native JSON parsing. Normal ClickClack REST payloads should be far smaller than 16 MiB.

Why acceptable

This matches the existing provider-response guardrail used elsewhere in OpenClaw and avoids creating a ClickClack-specific policy. The existing error-body limit and all public client method contracts are unchanged.

Maintainer-ready confidence

High. The patch is limited to the ClickClack REST response boundary, uses the shared bounded-reader helper, and has a real loopback proof that verifies both the overflow error and early stream close.

Patch quality notes

  • Production diff is one shared-reader import plus one success-path return change.
  • Test diff adds the loopback oversized success-response regression beside the existing bounded error-response test.
  • No repository evidence files are included in the commit.

Competition / linked PR analysis

Fresh pre-submit comparison against Alix-007 open PRs found no ClickClack file overlap. The current Alix-007 open bounded-response work covers Runway, OpenAI video, Together/Pixverse, XAI, image generation, MiniMax, FAL, Vydra, Speech, OpenRouter, embedding, Mattermost, Nextcloud, Inworld, and Discord, but no extensions/clickclack path.

A public open-PR text search for clickclack returned older docs/setup/config/channel PRs and did not show a bounded ClickClack REST response-body PR competing with this change.

@clawsweeper

clawsweeper Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 27, 2026, 10:24 AM ET / 14:24 UTC.

Summary
The PR routes ClickClack REST success JSON parsing through readProviderJsonResponse and adds a loopback oversized-response regression test.

PR surface: Source +3, Tests +94. Total +97 across 2 files.

Reproducibility: yes. Current main clearly routes ClickClack REST success responses through raw response.json(), and the PR body plus amended test describe the loopback oversized-response path that now fails at the shared cap.

Review metrics: 1 noteworthy metric.

  • Response-size policy change: 1 success JSON path capped at 16 MiB. The single ClickClack REST success helper now enforces the shared provider JSON cap for all successful REST methods, which is the main compatibility point before merge.

Root-cause cluster
Relationship: canonical
Canonical: #96970
Summary: This PR is the canonical ClickClack REST success JSON bounded-reader patch; related merged PRs cover sibling provider response paths only.

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] Maintainers should explicitly accept the shared 16 MiB ClickClack success-response cap before merge.

Risk before merge

  • [P1] Existing ClickClack success JSON responses larger than the shared 16 MiB provider cap will now fail early instead of being parsed through native response.json(), which is a deliberate compatibility boundary maintainers should accept before merge.

Maintainer options:

  1. Accept The Shared Provider JSON Cap (recommended)
    Proceed with the current implementation if maintainers agree ClickClack REST success JSON should follow the existing shared 16 MiB provider-response policy.
  2. Choose A ClickClack-Specific Limit First
    Pause merge and define a different ClickClack-specific cap only if legitimate ClickClack success responses are expected to exceed the shared provider JSON limit.

Next step before merge

  • No automated repair remains; the next action is maintainer acceptance of the compatibility boundary and normal merge readiness checks.

Security
Cleared: No concrete security or supply-chain issue found; the diff changes no dependencies, workflows, lockfiles, secrets handling, or downloaded code execution paths.

Review details

Best possible solution:

Land the shared bounded-reader helper change once maintainers accept the 16 MiB success-response cap for ClickClack REST responses and the current green checks remain tied to the latest head.

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

Yes. Current main clearly routes ClickClack REST success responses through raw response.json(), and the PR body plus amended test describe the loopback oversized-response path that now fails at the shared cap.

Is this the best way to solve the issue?

Yes. The best implementation point is the single ClickClack request<T>() helper because every REST success method flows through it; the latest test hardening now proves the intended early close rather than only checking a loose byte ceiling.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 9d800b71c060.

Label changes

Label changes:

  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes after-fix loopback live output from the real ClickClack client and native fetch stream showing the bounded-reader overflow error and early server close.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P2: This is a focused bundled-plugin hardening fix with limited blast radius and no current blocking code defect.
  • merge-risk: 🚨 compatibility: The patch intentionally changes oversized ClickClack success JSON responses from unbounded native parsing to early failure at the shared 16 MiB provider cap.
  • 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 (live_output): The PR body includes after-fix loopback live output from the real ClickClack client and native fetch stream showing the bounded-reader overflow error and early server close.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix loopback live output from the real ClickClack client and native fetch stream showing the bounded-reader overflow error and early server close.
Evidence reviewed

PR surface:

Source +3, Tests +94. Total +97 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 5 2 +3
Tests 1 94 0 +94
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 99 2 +97

What I checked:

Likely related people:

  • vincentkoc: The live PR is assigned to Vincent Koc, and the latest branch includes his test(clickclack): harden response cap proof commit that repaired the previous loopback assertion issue. (role: recent area contributor; confidence: medium; commits: 163d01db86f3; files: extensions/clickclack/src/http-client.test.ts)
  • mushuiyu886: The author also carried the merged sibling bounded-reader OpenRouter work and opened this focused ClickClack patch, so they are relevant for follow-up on this response-size guardrail. (role: adjacent bounded-reader contributor; confidence: medium; commits: 9c497b113077, 5715b5500067; files: extensions/clickclack/src/http-client.ts, extensions/openrouter/video-model-catalog.ts)
  • github-actions[bot]: In this shallow grafted checkout, blame/log attribute the current ClickClack client import to the grafted ClickClack plugin commit, so this is only a low-confidence routing signal rather than a human owner. (role: introduced current checked-out ClickClack helper; confidence: low; commits: c6757d7a75af; files: extensions/clickclack/src/http-client.ts, extensions/clickclack/src/http-client.test.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. labels Jun 26, 2026
@vincentkoc vincentkoc self-assigned this Jun 27, 2026
@vincentkoc
vincentkoc force-pushed the fix/followup-96505 branch 2 times, most recently from 4a89e2c to 638a780 Compare June 27, 2026 14:01
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed 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. labels Jun 27, 2026
@vincentkoc
vincentkoc force-pushed the fix/followup-96505 branch from 638a780 to 78f9be6 Compare June 27, 2026 14:11
@vincentkoc
vincentkoc force-pushed the fix/followup-96505 branch from 78f9be6 to 163d01d Compare June 27, 2026 14:14
@clawsweeper clawsweeper Bot added 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: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 27, 2026
@vincentkoc

Copy link
Copy Markdown
Member

Maintainer review and merge prep done.

Evidence:

  • Reviewed extensions/clickclack/src/http-client.ts, extensions/clickclack/src/http-client.test.ts, the ClickClack gateway/resolver/outbound callers, and the shared provider HTTP bounded-reader helper.
  • Current-main repro failed as expected because the ClickClack success path still called raw response.json().
  • Maintainer test hardening pushed on this PR: the loopback proof now streams a valid oversized JSON document with Node backpressure, avoids timer-sensitive byte assertions, and satisfies oxlint.
  • node scripts/run-vitest.mjs extensions/clickclack/src/http-client.test.ts passed after final rebase: 1 file / 2 tests.
  • node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.extensions.json extensions/clickclack/src/http-client.ts extensions/clickclack/src/http-client.test.ts passed.
  • node scripts/run-bundled-extension-oxlint.mjs passed.
  • node_modules/.bin/oxfmt --check extensions/clickclack/src/http-client.test.ts extensions/clickclack/src/http-client.ts passed.
  • git diff --check origin/main...HEAD passed.
  • Final autoreview with Codex gpt-5 passed cleanly with no accepted/actionable findings.
  • Hosted CI run 28291701410 completed successfully for exact head 163d01db86f3b7bc38b63190f7941b606e016803.
  • OPENCLAW_TESTBOX=1 scripts/pr prepare-run 96970 passed with hosted exact-head gates.

Maintainer decision accepted: ClickClack REST success JSON over the shared 16 MiB provider cap now fails early and cancels the response stream. Error response limiting, auth/header setup, request URLs, target/gateway callers, and payload unwrapping are unchanged.

Known proof gap: no credential-backed live ClickClack API call was run; this PR changes the response-body reader boundary, which is covered by loopback streamed response tests.

@vincentkoc
vincentkoc merged commit f4fa10c into openclaw:main Jun 27, 2026
104 of 109 checks passed
@vincentkoc

Copy link
Copy Markdown
Member

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 28, 2026
* fix(clickclack): bound REST success JSON response reads

* test(clickclack): harden response cap proof

---------

Co-authored-by: Vincent Koc <[email protected]>
@clawsweeper

clawsweeper Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper could not start a re-review for this item.

Reason: re-review requires an open issue or PR.

QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
* fix(clickclack): bound REST success JSON response reads

* test(clickclack): harden response cap proof

---------

Co-authored-by: Vincent Koc <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
* fix(clickclack): bound REST success JSON response reads

* test(clickclack): harden response cap proof

---------

Co-authored-by: Vincent Koc <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
* fix(clickclack): bound REST success JSON response reads

* test(clickclack): harden response cap proof

---------

Co-authored-by: Vincent Koc <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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