Skip to content

[Fix] Billed API requests return no response when streams fail silently or end at max_tokens - #1580

Draft
zoomote[bot] wants to merge 1 commit into
mainfrom
fix/silent-retry-stop-reason-thinking-signature-1up978exiqscg
Draft

[Fix] Billed API requests return no response when streams fail silently or end at max_tokens#1580
zoomote[bot] wants to merge 1 commit into
mainfrom
fix/silent-retry-stop-reason-thinking-signature-1up978exiqscg

Conversation

@zoomote

@zoomote zoomote Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

​Created by Roomote. Follow up by mentioning @roomote, in the web UI, or in Discord.

Related GitHub Issue

Reported in Discord (no tracking issue): users are billed for API requests that produce no response — recurring streaming_failed rows with nothing shown, observed primarily on Sonnet.

Description

Three related causes of "billed but no response" are fixed together:

1. Silent, unbounded mid-stream retries (src/core/task/Task.ts)
When a streaming request failed mid-flight (e.g. Anthropic overloaded_error), the retry loop pushed the same request back onto the stack unconditionally; the backoff countdown was gated on autoApprovalEnabled, so most users saw nothing while every retry re-billed the full input context. Now automatic mid-stream retries are capped at MAX_AUTOMATIC_API_RETRIES = 3, every retry runs through the visible backoffAndAnnounceapi_req_retry_delayed countdown regardless of auto-approval, and once the cap is exhausted the user is asked via api_req_failed. Approving resets the retry budget (the turn's user message is popped and re-added exactly once, so history never duplicates); declining surfaces the error and records a synthetic failure assistant message before stopping.

2. stop_reason was never inspected (src/api/transform/stream.ts, src/api/providers/anthropic.ts, src/core/task/Task.ts)
Anthropic's message_delta carries stop_reason, which was dropped. It now flows through the usage stream chunk, and the empty/no-tool-use response path branches on it: a response that ends with max_tokens and no usable content (e.g. the model burned its whole output budget on thinking) is not retried — retrying the identical request would fail the same way while re-billing the full context. The user gets a clear error explaining the token limit and suggesting a higher max output tokens setting. Genuinely empty responses keep automatic retries, now bounded by the same cap before asking the user.

3. Anthropic thinking signatures were dropped (src/api/providers/anthropic.ts, src/core/task/apiConversationHistory.ts)
signature_delta stream events were ignored, so thinking blocks could never be replayed on tool-use continuations (Anthropic rejects unsigned replays). The handler now accumulates each thinking block's text and signature, emits the existing thinking_complete stream chunk per completed block, and exposes getThinkingBlocks()/getThoughtSignature(). The existing history plumbing replays each signed thinking block individually (signatures only validate against their exact block text, so blocks are never combined under one signature). filterNonAnthropicBlocks already preserves thinking/redacted_thinking via its allowlist; a regression test now pins that.

Test Procedure

cd src
pnpm exec vitest run core/task/__tests__/Task.spec.ts core/task/__tests__/apiConversationHistory.spec.ts api/providers/__tests__/anthropic.spec.ts   # 189/189 passing
pnpm exec tsc --noEmit   # clean
pnpm exec eslint --prune-suppressions --max-warnings=0 core/task/Task.ts core/task/__tests__/Task.spec.ts core/task/apiConversationHistory.ts core/task/__tests__/apiConversationHistory.spec.ts api/providers/anthropic.ts api/providers/__tests__/anthropic.spec.ts api/transform/stream.ts   # clean, no suppression-count changes
pnpm test   # full suite: 10/10 turbo tasks, 8075 passed / 39 skipped

New coverage:

  • Task.spec.ts — mid-stream suite: cap-then-ask with exactly 3 visible retry announcements and exact attempt counts; visibility without auto-approval; user-approved budget reset re-adds the user message exactly once; declined retry restores history and records the failure.
  • Task.spec.ts — empty-response suite: stop_reason: max_tokens stops immediately with the remediation error (no retry, no ask, history consistent); automatic empty-response retries are bounded and then ask.
  • anthropic.spec.tsstop_reason propagation on usage chunks; signature_delta capture with thinking_complete emission and getThoughtSignature(); per-block pairing across multiple thinking blocks; stale signature reset between requests; signed thinking/redacted_thinking blocks round-trip unchanged.
  • apiConversationHistory.spec.ts — each thinking block is replayed with its own signature, in order.

Pre-Submission Checklist

  • Issue Linked: Reported via Discord support thread; no GitHub issue exists yet.
  • Scope: My changes are focused on the linked issue (one major feature/fix per PR).
  • Self-Review: I have performed a thorough self-review of my code.
  • Testing: New and/or updated tests have been added to cover my changes (see "Test Procedure").
  • Visual Snapshot (UI changes only): N/A — no UI changes.
  • Documentation Impact: I have considered if my changes require documentation updates (see "Documentation Updates" section below).
  • Contribution Guidelines: I have read and agree to the Contributor Guidelines.

Visual Snapshots

N/A — extension-host retry/streaming logic with no rendered-UI change.

Videos (interaction / animation only)

N/A

Documentation Updates

  • No documentation updates are required. (The docs do not describe retry/error-handling behavior or stop_reason semantics, and this fixes broken behavior without adding settings or features.)
  • Yes, documentation updates are required.

Additional Notes

Consolidates what would otherwise be three separate PRs (mid-stream retry bounding, stop_reason handling, thinking-signature preservation) into one reviewable change. The same signature_delta/stop_reason gaps also exist in anthropic-vertex.ts, which duplicates this streaming loop; left out of scope here and a good follow-up.

Get in Touch

@taltas on Discord can reach the requester (on55db) for reproduction details.

…ies, unhandled max_tokens stops, and dropped thinking signatures

- Bound mid-stream API failure retries (3 automatic attempts), announce
  every retry through the visible backoff countdown, and ask the user
  once the budget is exhausted instead of looping silently.
- Propagate the response stop_reason through the usage stream and stop
  retrying when an empty response ended with max_tokens, surfacing
  remediation guidance instead of re-billing the full context.
- Capture Anthropic thinking-block signatures (signature_delta) and
  replay each signed thinking block unchanged on tool-use
  continuations.
@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 40 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 15c77b26-a300-48ff-b657-dd3fe6d6af04

📥 Commits

Reviewing files that changed from the base of the PR and between 134923e and e7c1d58.

📒 Files selected for processing (7)
  • src/api/providers/__tests__/anthropic.spec.ts
  • src/api/providers/anthropic.ts
  • src/api/transform/stream.ts
  • src/core/task/Task.ts
  • src/core/task/__tests__/Task.spec.ts
  • src/core/task/__tests__/apiConversationHistory.spec.ts
  • src/core/task/apiConversationHistory.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Review status

This PR was opened by an automated account. A human maintainer must verify the change intent, provenance, and validation before merging.

Current step: Mark the PR ready. Required CI must pass before CodeRabbit starts.

Review-state labels are managed by this workflow; do not edit them manually.

@codecov

codecov Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.47619% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/core/task/Task.ts 86.11% 0 Missing and 5 partials ⚠️
src/api/providers/anthropic.ts 95.45% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@edelauna

edelauna commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@edelauna

Copy link
Copy Markdown
Contributor

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants