Skip to content

fix(provider-usage): bound Anthropic usage error response reads to prevent OOM#97614

Merged
vincentkoc merged 1 commit into
openclaw:mainfrom
cxbAsDev:fix/bound-claude-usage-error-response
Jun 29, 2026
Merged

fix(provider-usage): bound Anthropic usage error response reads to prevent OOM#97614
vincentkoc merged 1 commit into
openclaw:mainfrom
cxbAsDev:fix/bound-claude-usage-error-response

Conversation

@cxbAsDev

Copy link
Copy Markdown
Contributor

Summary

Replace unbounded res.json() with readProviderJsonResponse in the fetchClaudeUsage error handler. This caps Anthropic usage API error response bodies at 16 MiB, preventing unbounded buffering when the API returns a large error body (CDN error pages, WAF blocks, misconfigured proxies, etc.).

Real behavior proof (required for external PRs)

Behavior addressed: Unbounded res.json() replaced with readProviderJsonResponse (16 MiB cap) in the error handler path.

Real environment tested: Linux, Node 22, OpenClaw main @ 87db23e

Exact steps or command run after fix:

node --import tsx proof-claude-usage-error.mts

Evidence after fix:

cap: 16777216 bytes (16 MiB)
chunks enqueued: 17
stream cancelled: true
error: Anthropic usage error: JSON response exceeds 16777216 bytes
data: null
PASS: reader stopped at 17.0 MiB, stream cancelled before full 64 MiB buffering

The harness creates a streaming 64 MiB error body with 1 MiB chunks. The bounded reader pulls only 17 chunks (stopping at the 16 MiB cap), cancels the stream, and throws. The existing catch block catches this and returns the error snapshot. Before the fix, res.json() would have buffered the entire 64 MiB before parsing.

Observed result after fix: readProviderJsonResponse caps Anthropic usage error bodies at 16 MiB. Streaming error bodies are cancelled at the cap. The existing catch block ("ignore parse errors") now also handles overflow errors, preserving the existing fallback behavior (scope-check → web fallback → HTTP error snapshot).

All existing tests pass: 14 tests in provider-usage.fetch.claude.test.ts.

What was not tested: A live Anthropic usage API call returning a large error body was not tested.

Risk

Low. The 16 MiB cap matches the convention in provider-http-errors.ts. The existing catch block gracefully handles both parse errors (existing) and overflow errors (new), preserving the same fallback behavior.

…event OOM

Replace unbounded res.json() with readProviderJsonResponse in the
fetchClaudeUsage error path to cap error body reads at 16 MiB.
@clawsweeper

clawsweeper Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 28, 2026, 9:55 PM ET / 01:55 UTC.

Summary
The PR imports readProviderJsonResponse into fetchClaudeUsage and uses it to parse non-OK Anthropic OAuth usage error bodies under the existing 16 MiB provider JSON cap.

PR surface: Source +1. Total +1 across 1 file.

Reproducibility: yes. Source inspection shows current main calls res.json() on the non-OK Claude OAuth usage response, and a mocked 403 Response with an oversized streamed JSON body can exercise the unbounded read path; I did not run the harness in this read-only sweep.

Review metrics: none identified.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
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:

  • Consider adding a focused overflow regression in src/infra/provider-usage.fetch.claude.test.ts if maintainers want CI coverage for this exact error path.

Risk before merge

  • [P1] No committed regression test exercises an oversized Claude OAuth error body on this exact path; review confidence relies on the PR-body terminal proof plus the existing bounded-helper tests.

Maintainer options:

  1. Decide the mitigation before merge
    Merge this narrow Claude OAuth error-path hardening after normal maintainer checks, while keeping shared success-reader and MiniMax follow-ups in their own focused PRs.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P2] No repair lane is needed because there are no actionable code findings; the remaining path is ordinary maintainer review and exact-head validation.

Security
Cleared: The diff reuses an existing bounded JSON reader and does not change dependencies, workflows, package metadata, permissions, or secret-handling surfaces.

Review details

Best possible solution:

Merge this narrow Claude OAuth error-path hardening after normal maintainer checks, while keeping shared success-reader and MiniMax follow-ups in their own focused PRs.

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

Yes. Source inspection shows current main calls res.json() on the non-OK Claude OAuth usage response, and a mocked 403 Response with an oversized streamed JSON body can exercise the unbounded read path; I did not run the harness in this read-only sweep.

Is this the best way to solve the issue?

Yes. Reusing readProviderJsonResponse at this call site is the narrowest maintainable fix because the helper already owns the byte cap, JSON parse behavior, and stream cancellation contract.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 4a4657a1828f.

Label changes

Label changes:

  • add P2: This is normal-priority provider-usage availability hardening for one external error-response path with limited blast radius.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes copied after-fix terminal output from a Linux Node 22 harness showing a 64 MiB streamed error body is stopped at the 16 MiB cap and cancelled before full buffering.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes copied after-fix terminal output from a Linux Node 22 harness showing a 64 MiB streamed error body is stopped at the 16 MiB cap and cancelled before full buffering.

Label justifications:

  • P2: This is normal-priority provider-usage availability hardening for one external error-response path with limited blast radius.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit 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 copied after-fix terminal output from a Linux Node 22 harness showing a 64 MiB streamed error body is stopped at the 16 MiB cap and cancelled before full buffering.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes copied after-fix terminal output from a Linux Node 22 harness showing a 64 MiB streamed error body is stopped at the 16 MiB cap and cancelled before full buffering.
Evidence reviewed

PR surface:

Source +1. Total +1 across 1 file.

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

What I checked:

  • Repository policy read: Root AGENTS.md was read fully; no src/infra scoped AGENTS.md exists, and src/agents/AGENTS.md was read because the patch imports the provider HTTP helper from src/agents. (AGENTS.md:1, 4a4657a1828f)
  • Current main behavior: On current main, fetchClaudeUsage still calls res.json() in the non-OK Anthropic OAuth usage response path before deciding whether to use the web fallback or return an HTTP error snapshot. (src/infra/provider-usage.fetch.claude.ts:154, 4a4657a1828f)
  • PR source change: The PR replaces that direct res.json() call with readProviderJsonResponse<{ error?: { message?: unknown } | null }>(res, "Anthropic usage error"), preserving the existing message extraction and catch behavior. (src/infra/provider-usage.fetch.claude.ts:152, 14367e456f09)
  • Bounded helper contract: readProviderJsonResponse defaults to the provider JSON response cap, reads through readResponseWithLimit, throws on overflow, and wraps malformed JSON with the caller label. (src/agents/provider-http-errors.ts:312, 4a4657a1828f)
  • Overflow cancellation contract: readResponseWithLimit cancels the body reader when the next chunk would exceed the byte cap, then throws the caller-provided overflow error. (packages/media-core/src/read-response-with-limit.ts:96, 4a4657a1828f)
  • Helper test coverage: Existing provider HTTP tests already cover oversized streamed JSON responses through readProviderJsonResponse and assert the reader stops before consuming all chunks. (src/agents/provider-http-errors.test.ts:277, 4a4657a1828f)

Likely related people:

  • vincentkoc: Recent commits changed the provider-usage Claude/shared fetchers, including malformed JSON handling and response-body cancellation around the same error and fetch paths. (role: recent area contributor; confidence: high; commits: a118e114fe5f, 0c565f3b0ef9; files: src/infra/provider-usage.fetch.claude.ts, src/infra/provider-usage.fetch.shared.ts, src/infra/provider-usage.fetch.shared.test.ts)
  • steipete: Earlier provider-usage refactors deduplicated the shared fetch/helper structure that this PR now extends for the Claude-specific error path. (role: feature-history owner; confidence: high; commits: badafdc7b3e4, 14b4c7fd56ae; files: src/infra/provider-usage.fetch.shared.ts, src/infra/provider-usage.fetch.claude.ts, src/infra/provider-usage.fetch.ts)
  • Alix-007: The merged provider HTTP hardening PR introduced the bounded provider JSON helper this patch reuses. (role: adjacent helper contributor; confidence: medium; commits: 2592f8a51a4e; files: src/agents/provider-http-errors.ts, src/agents/provider-http-errors.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. labels Jun 29, 2026
@vincentkoc
vincentkoc merged commit 615558f into openclaw:main Jun 29, 2026
48 of 53 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 29, 2026
…event OOM (openclaw#97614)

Replace unbounded res.json() with readProviderJsonResponse in the
fetchClaudeUsage error path to cap error body reads at 16 MiB.
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
…event OOM (openclaw#97614)

Replace unbounded res.json() with readProviderJsonResponse in the
fetchClaudeUsage error path to cap error body reads at 16 MiB.
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
…event OOM (openclaw#97614)

Replace unbounded res.json() with readProviderJsonResponse in the
fetchClaudeUsage error path to cap error body reads at 16 MiB.
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
…event OOM (openclaw#97614)

Replace unbounded res.json() with readProviderJsonResponse in the
fetchClaudeUsage error path to cap error body reads at 16 MiB.
Rorqualx pushed a commit to Rorqualx/cortex that referenced this pull request Jul 5, 2026
…event OOM (openclaw#97614)

Replace unbounded res.json() with readProviderJsonResponse in the
fetchClaudeUsage error path to cap error body reads at 16 MiB.

(cherry picked from commit 615558f)
Rorqualx pushed a commit to Rorqualx/cortex that referenced this pull request Jul 6, 2026
…event OOM (openclaw#97614)

Replace unbounded res.json() with readProviderJsonResponse in the
fetchClaudeUsage error path to cap error body reads at 16 MiB.

(cherry picked from commit 615558f)
Rorqualx pushed a commit to Rorqualx/cortex that referenced this pull request Jul 8, 2026
…event OOM (openclaw#97614)

Replace unbounded res.json() with readProviderJsonResponse in the
fetchClaudeUsage error path to cap error body reads at 16 MiB.

(cherry picked from commit 615558f)
Rorqualx pushed a commit to Rorqualx/cortex that referenced this pull request Jul 8, 2026
…event OOM (openclaw#97614)

Replace unbounded res.json() with readProviderJsonResponse in the
fetchClaudeUsage error path to cap error body reads at 16 MiB.

(cherry picked from commit 615558f)
@cxbAsDev
cxbAsDev deleted the fix/bound-claude-usage-error-response branch July 15, 2026 03:32
wheakerd pushed a commit to wheakerd/clawdbot that referenced this pull request Jul 15, 2026
…event OOM (openclaw#97614)

Replace unbounded res.json() with readProviderJsonResponse in the
fetchClaudeUsage error path to cap error body reads at 16 MiB.

(cherry picked from commit 615558f)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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: XS 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