Skip to content

fix(chutes): bound OAuth token error response reads#97808

Merged
vincentkoc merged 2 commits into
openclaw:mainfrom
Pick-cat:fix/chutes-bound-oauth-error-reads
Jun 29, 2026
Merged

fix(chutes): bound OAuth token error response reads#97808
vincentkoc merged 2 commits into
openclaw:mainfrom
Pick-cat:fix/chutes-bound-oauth-error-reads

Conversation

@Pick-cat

@Pick-cat Pick-cat commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Chutes OAuth token exchange and refresh failures read the error body with an unbounded await response.text(). On a hostile or misconfigured token endpoint (https://api.chutes.ai/idp/token) that streams a large body with no Content-Length, the gateway buffers the entire payload into memory before surfacing the error — a memory-pressure / OOM vector during auth setup and token refresh.

Affected surface: src/agents/chutes-oauth.tsexchangeChutesCodeForTokens and refreshChutesTokens error paths.

Why This Change Was Made

Successful Chutes JSON token responses are already bounded via readProviderJsonResponse, but the two !response.ok branches still called response.text() directly. This change routes those reads through the existing shared bounded reader readResponseTextLimited with an 8 KiB cap (CHUTES_OAUTH_ERROR_BODY_LIMIT_BYTES), matching the MiniMax OAuth error-handling pattern. The reader stops pulling and cancels the upstream stream once the diagnostic budget is full, so the surfaced error snippet stays useful while the body can no longer grow without bound.

No new abstraction — reuses the existing readResponseTextLimited helper. One concern, two call sites; chutes-oauth.ts does not touch fs-safe or any other surface.

User Impact

Operators configuring or refreshing Chutes OAuth credentials still get the same actionable error snippet on token failures, but the gateway can no longer be driven to buffer an arbitrarily large error body into memory by a hostile or broken token endpoint.

Evidence

1. Real-behavior proof with a negative control. A harness drives the real exported exchangeChutesCodeForTokens / refreshChutesTokens against a real node:http server on 127.0.0.1 that streams a 25 MiB error body in chunks with no Content-Length, and measures bytes actually flushed on the wire before the socket closes. The negative control runs the pre-fix await response.text() against the same server.

$ tsx chutes-proof.mts
CHUTES_TOKEN_ENDPOINT = https://api.chutes.ai/idp/token
Hostile error body = 25.00 MiB, streamed with NO Content-Length

[WITH FIX] exchangeChutesCodeForTokens
PASS: error surfaced — "Chutes token exchange failed: chutes error body chutes error..."
PASS: error snippet bounded — error message = 8222 bytes
PASS: upstream stopped early (cap load-bearing) — server flushed only 0.21 MiB before socket closed

[WITH FIX] refreshChutesTokens
PASS: error surfaced — "Chutes token refresh failed: chutes error body chutes error ..."
PASS: upstream stopped early (cap load-bearing) — server flushed only 0.21 MiB before socket closed

[NEGATIVE CONTROL] old `await response.text()` on same server
PASS: old path buffers entire body — response.text() returned 25.03 MiB (server sent 25.03 MiB)

ALL PROOF ASSERTIONS PASSED

Without the fix the error read returns the full 25.03 MiB body; with the fix the surfaced snippet is capped at 8222 bytes and the server flushes only 0.21 MiB before the client cancels the stream and the socket closes — proving the cap is load-bearing, not incidental. The residual ~0.21 MiB is in-flight TCP/socket buffering, not buffered by the reader.

2. Committed regression tests (src/agents/chutes-oauth.flow.test.ts) cover both call sites: they stream an oversized error body through a tracked ReadableStream, spy on response.text() to assert it is never called, and assert the stream is canceled after the bounded read.

$ node scripts/run-vitest.mjs src/agents/chutes-oauth.flow.test.ts
 Test Files  1 passed (1)
      Tests  8 passed (8)

3. Lint + types on the changed file:

$ node scripts/run-oxlint.mjs src/agents/chutes-oauth.ts   # exit 0
$ pnpm tsgo                                                 # no chutes type errors

What was NOT tested: I did not hit the live Chutes endpoint and did not drive the process to an actual OOM. The cap is proven by bytes-on-wire + early socket close against a local server reproducing the unbounded-stream condition, plus the negative control showing the old path buffering the full body.

AI-assisted: implementation, tests, and the proof harness were drafted with agent assistance; all commands above were run locally.

Fixes the unbounded Chutes OAuth error-body read.

@Pick-cat
Pick-cat requested a review from a team as a code owner June 29, 2026 14:18
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Jun 29, 2026
@clawsweeper

clawsweeper Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 29, 2026, 11:25 AM ET / 15:25 UTC.

Summary
The PR replaces two Chutes OAuth error-path response.text() reads with a bounded shared reader and adds exchange/refresh cancellation regression tests.

PR surface: Source +2, Tests +78. Total +80 across 2 files.

Reproducibility: yes. Current main has source-visible unbounded response.text() reads in both Chutes token non-OK branches, and the PR body provides a local streaming-server negative control for the same attack shape.

Review metrics: none identified.

Root-cause cluster
Relationship: canonical
Canonical: #97808
Summary: This PR is the canonical open item for the remaining core Chutes OAuth token error-body reads; related merged PRs covered sibling JSON-read or adjacent bounded-read surfaces.

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:

  • none.

Next step before merge

  • No automated repair is indicated; the focused patch appears correct and ready for normal maintainer review.

Security
Cleared: The diff narrows an auth memory-pressure path and does not add dependency, workflow, secret, permission, or package-resolution surface.

Review details

Best possible solution:

Land the owner-local bounded-read fix once normal maintainer review is complete; no broader OAuth refactor or new config surface is needed.

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

Yes. Current main has source-visible unbounded response.text() reads in both Chutes token non-OK branches, and the PR body provides a local streaming-server negative control for the same attack shape.

Is this the best way to solve the issue?

Yes. Reusing the existing bounded reader in the two owner-local token error branches matches sibling Chutes plugin and MiniMax OAuth patterns without adding new API or config surface.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 1052652a7168.

Label changes

Label justifications:

  • P2: The PR targets a bounded auth/OAuth memory-pressure bug with limited code surface.
  • 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 output from a real local HTTP server harness with a negative control for this non-visual runtime path.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal output from a real local HTTP server harness with a negative control for this non-visual runtime path.
Evidence reviewed

PR surface:

Source +2, Tests +78. Total +80 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 5 3 +2
Tests 1 78 0 +78
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 83 3 +80

What I checked:

Likely related people:

  • wangmiao0668000666: Blame and file history tie the current Chutes OAuth error branches, shared bounded reader, and recent core/plugin Chutes JSON bounded-read work to this author. (role: introduced and recent adjacent contributor; confidence: high; commits: e4e4b0161f3e, f83cdec21791, 5723222bbb3c; files: src/agents/chutes-oauth.ts, src/agents/provider-http-errors.ts, extensions/chutes/oauth.ts)
  • Wynne668: Blame shows recent cleanup-context comments inside readResponseTextLimited, the shared helper this PR reuses. (role: recent helper contributor; confidence: medium; commits: ac5af250154d; files: src/agents/provider-http-errors.ts)
  • vincentkoc: Live PR metadata shows this user merged the related Chutes core, Chutes plugin, and provider-usage bounded-read PRs. (role: merger of adjacent bounded-read work; confidence: medium; commits: f83cdec21791, 5723222bbb3c, 615558f6fb3f; files: src/agents/chutes-oauth.ts, extensions/chutes/oauth.ts, 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. labels Jun 29, 2026
@Pick-cat

Copy link
Copy Markdown
Contributor Author

Updated the PR body with real-behavior proof: the real exported exchangeChutesCodeForTokens / refreshChutesTokens driven against a local node:http server streaming a 25 MiB error body with no Content-Length, plus a negative control. With the fix the surfaced snippet caps at 8222 bytes and the server flushes only ~0.21 MiB before the stream is canceled; the pre-fix response.text() buffers the full 25.03 MiB. Details + commands in Evidence.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 29, 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.

@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 29, 2026
@vincentkoc
vincentkoc merged commit 9949f6b into openclaw:main Jun 29, 2026
94 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 30, 2026
* fix(chutes): bound OAuth token error response reads

* ci: re-trigger checks (fs-safe unhandled-rejection flake on prior run)

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Pick-cat <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
* fix(chutes): bound OAuth token error response reads

* ci: re-trigger checks (fs-safe unhandled-rejection flake on prior run)

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Pick-cat <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
* fix(chutes): bound OAuth token error response reads

* ci: re-trigger checks (fs-safe unhandled-rejection flake on prior run)

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Pick-cat <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
* fix(chutes): bound OAuth token error response reads

* ci: re-trigger checks (fs-safe unhandled-rejection flake on prior run)

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Pick-cat <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
wheakerd pushed a commit to wheakerd/clawdbot that referenced this pull request Jul 15, 2026
* fix(chutes): bound OAuth token error response reads

* ci: re-trigger checks (fs-safe unhandled-rejection flake on prior run)

Co-Authored-By: Claude Opus 4.8 <[email protected]>

---------

Co-authored-by: Pick-cat <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
(cherry picked from commit 9949f6b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling 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