Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
This stack of pull requests is managed by Graphite. Learn more about stacking. |
fddd1b2 to
7a489de
Compare
277bf6d to
3dfc063
Compare
7a489de to
bcbc7de
Compare
3dfc063 to
0c7f604
Compare
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
bcbc7de to
ec360b2
Compare
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
WalkthroughThis pull request extends error handling throughout the chat interface to distinguish between total rate limits and concurrency throttles, and to support wallet-locked account states. The 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. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |

[Billing Credits] PR-I4: Wallet-locked + concurrency error states
The server now returns two new error states the inspector currently renders
generically. This PR threads the new fields through the chat error parser
and renders distinct UX for each, plus tightens one top-up dialog edge case.
What's new
1. Wallet-locked banner — when the server marks an account as paused
(
walletLocked: true), the chat error renders a dedicated "Account underreview" banner with a contact-support link. No top-up button, no retry —
the user can't self-serve out of this state.
2. Concurrency-throttle retry UI — when the server returns a
rate-limit with
limitKind: "concurrency", the chat error renders atransient banner with a small Retry button and a seconds-level
countdown. No top-up CTA (topping up doesn't help — the user just needs
to wait).
3. Existing rate-limit + canTopUp path — unchanged. Daily quota
exhausted with a paid wallet option still shows the existing
"Top up to keep chatting" CTA.
Implementation
chat-helpers.ts:FormattedErrorgains three optional fields(
walletLocked?: boolean,limitKind?: "total" | "concurrency",retryAfterMs?: number). Both parser branches (rate-limit andgeneric-structured) defensively extract them.
retryAfterMsisemitted only for the concurrency case so the existing
toEqualassertions on rate-limit results stay tight.
error.tsx: two new early-return branches at the top of thecomponent for the locked and concurrency states. The existing
rate-limit / generic / auth-error paths are unchanged.
ChatTabV2.tsx: a newhandleRetryConcurrencyMessagecallbackreuses the existing
lastSentUserMessageRefto resubmit the user'slast typed message. The
onRetryprop is gated on the error being aconcurrency throttle so unrelated retryable errors don't surface a
Retry button.
useCreditTopup.ts: thestartCheckoutcatch block now detectsthe server's "Too many top-up attempts" message and re-throws a
sanitized "You've hit the top-up rate limit. Try again in a few
minutes." The dialog's existing toast picks up the new message, so
no double-toast and no unhandled propagation.
Tests
Added 3 parser tests in
chat-helpers-clone.test.ts:limitKind+retryAfterMspass through together.The one existing assertion that turned out to be sensitive to the new
limitKind: "total"field (the JSON included it but the assertiondidn't) was extended to expect it.
error.tsxhas no existing render-test pattern, so per the spec thenew states get manual verification rather than fabricated tests.
Verification (manual)
walletLocked: true→ "Account under review"banner, no top-up, mailto link present.
limitKind: "concurrency"andretryAfter: 8000→transient banner with "Retry in 8 seconds" + Retry button. Click
Retry → resubmits the last typed user message.
createCreditCheckoutSessionrejecting with "Too manytop-up attempts. Try again in 5 minutes." → toast surfaces the
friendlier sanitized copy; dialog doesn't crash.
Local run of all touched suites green:
chat-helpers-clone.test.ts— 12 testsclient/src/components/billing— 23 tests across 5 filesclient/src/hooks/__tests__/useCreditTopup*— 19 tests across 2 filesclient/src/components/chat-v2/**andOrganizationsTab.billing.test.tsx— 444 tests across 38 filesStack
This PR is a logical follow-up to PR-I3 (the existing billing-credits
stack), but submitted independently against
mainrather than stacked.It builds on the
FormattedErrorinfrastructure introduced in PR-I1, soexpect to rebase once the I-stack merges.
Rebased 2026-05-01 onto the redesigned PR-I3 tip — the I3 design
simplification (drop dollar values from the bars, retire the activity
table, switch to
hasPurchaseHistoryboolean) does not interact withthis PR's scope. Diff cleanly reapplied with no conflicts.
Risk
Low. All new fields are optional; missing or wrong-type values default
to
undefined(no crash, no false positives). The new UI states aregated on explicit fields, so legacy responses keep their existing
rendering. The dialog's existing error path remains the same — only the
message it surfaces is now sanitized.