Skip to content

fix(cloud): refund stranded hold on non-streaming app-chat post-provider failure (#11169 part 1) - #11278

Merged
lalalune merged 2 commits into
developfrom
fix/11169-nonstream-refund
Jul 2, 2026
Merged

lalalune merged 2 commits into
developfrom
fix/11169-nonstream-refund

Conversation

@lalalune

@lalalune lalalune commented Jul 2, 2026

Copy link
Copy Markdown
Member

Closes part 1 of #11169. The non-streaming branch of apps/[id]/chat read the provider body, computed cost, and settled — all outside any refund scope; a throw before the settle (truncated body failing providerResponse.json(), transient calculateCost error) fell through to the outer catch, which returned 500 WITHOUT refunding the upfront hold. The streaming branch was covered by reconcileStreamProcessingError (#10837); this path had nothing.

Extracts a pure reconcileNonStreamProcessingError helper (mirroring the stream one): wraps the post-provider block, tracks whether reconcileCredits settled, full-refunds iff the settle had not yet run — a body/cost failure can't keep the advertiser's credits, while a post-settle throw correctly keeps the real charge. 3 unit tests drive the real helper; existing stream-refund test still green; tsgo + biome clean.

Scope note: #11169 residual 2 (/v1/chat reserve sizing) already landed on develop; residuals 3 (stranded synchronous reservation on dropped waitUntil/eviction — needs a stale-reservation sweep mirroring sweepStalePendingInferenceChargesDb) and 4 (abort-mid-stream refund evasion) are the reservation-lifecycle sweep work, tracked on the issue for a follow-up.

🤖 Generated with Claude Code

Shaw and others added 2 commits July 2, 2026 03:56
…-provider processing fails (#11169 part 1)

The non-streaming branch of apps/[id]/chat read the provider body, computed
cost, and settled — all outside any refund scope. A throw before the settle
(truncated body failing providerResponse.json(), transient calculateCost error)
fell through to the outer catch, which returned 500 WITHOUT refunding the
upfront hold. The streaming branch was covered by reconcileStreamProcessingError
(#10837); this path was not.

Extracts a pure reconcileNonStreamProcessingError helper mirroring the stream
one: wraps the post-provider block, tracks whether reconcileCredits settled, and
full-refunds iff the settle had not yet run — so a body/cost failure can't keep
the advertiser's credits, while a post-settle throw correctly keeps the real
charge. 3 unit tests drive the real helper.

Residual 2 (/v1/chat reserve sizing) already landed on develop; residuals 3
(stranded synchronous reservation on dropped waitUntil) and 4 (abort-refund
evasion) need the reservation-lifecycle sweep and are tracked separately on
the issue.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a07a2bc4-68fd-4496-9599-eefb5df4bc9b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/11169-nonstream-refund

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.

@claude

claude Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Claude encountered an error —— View job


I'll analyze this and get back to you.

@github-actions github-actions Bot added the Tests label Jul 2, 2026
NubsCarson added a commit that referenced this pull request Jul 2, 2026
…d-settle throw can't double-refund (mint credits) (#11473)

The #11271 mass-revert reintroduced a double-credit on POST /api/v1/apps/:id/chat
non-streaming: #11278 re-fixed the stranded-hold refund but set the settle flag
AFTER reconcileCredits returns. reconcileCredits is not transactional — it commits
its org-balance refund before its earnings/counter writes — so a mid-settle throw
(e.g. reverseCreatorEarnings deadlock) reached the catch as 'never settled' and
refunded the FULL hold a second time (total credited = 2x reserved − actual).

Restore the #11218 fence: flip nonStreamingSettleStarted IMMEDIATELY BEFORE invoking
reconcileCredits and route the catch through reconcileNonStreamingSettleError
(settleStarted gating + non_streaming_settle_error ledger tag), wrapped in .catch so
a refund failure can't mask the original error. Collapse the divergent dead
reconcileNonStreamProcessingError helper (+ its test) into the single settle helper.

The route-level guard test (apps-chat-nonstreaming-settle-guard) was RED at develop
tip (3 fail/1 pass, reconcileCredits called 2x, masked by concurrency-cancelled CI);
now 4/4 green. apps-chat-stream-refund 7/7 green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant