feat(error): render per-kind error cards with localized copy and provider reason#1473
Conversation
|
Warning Review limit reached
More reviews will be available in 10 minutes and 6 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (13)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Suggested priority: P2 (includes user-path files (packages/app/src/pages/session/message-timeline.tsx)).
P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.
bcaec70 to
f7cae16
Compare
…ider reason Session errors rendered a single raw-text card, so a 402 "Insufficient Balance" surfaced to users as an opaque "Connection lost" line with no guidance. PR1-PR3 already classify provider failures into a canonical `providerFailure.kind` and stop overwriting the real reason; this PR makes the render path use that signal. ErrorCard maps each `providerFailure.kind` to one unified card shell: severity drives a 2px left rule and icon (red = must act for auth / quota_exhausted / invalid_request, amber = wait or resend for the rest), plain-language localized copy is the body, and the provider's verbatim reason sits in a collapsed "detail" disclosure. The `unknown` kind has no plain copy, so the decoded reason becomes the body with no detail. Red cards carry a single primary action (re-login / switch model) that opens the models settings tab; the app layer injects that side effect so packages/ui stays framework-agnostic. There is no retry button: no retry mechanism exists, so the user resends. Localized en / zh / zht copy with parity tests; the other locales fall back to en. Covered by a unit test over the presentation map, an i18n real-translation test, an E2E that proves the 402 -> card -> disclose reason -> open settings path, and a snap target for visual review.
f7cae16 to
c84d314
Compare
Summary
Session errors rendered as a single raw-text card, so a 402 "Insufficient Balance" reached users as an opaque "Connection lost" line with no guidance. This PR makes the render path consume the canonical
providerFailure.kindthat PR1-PR3 already classify and stop overwriting.A single
ErrorCard(packages/ui/src/components/error-card.tsx, backed by the presentation map inpackages/ui/src/util/error-card.ts) maps each kind to one unified shell:auth,quota_exhausted,invalid_request), amber = wait or resend (rate_limit,server_overload,transport_disconnect,decompression,unknown).unknownhas no plain copy, so the decoded reason becomes the body with no detail.RateLimitCardWiring) sopackages/uistays framework-agnostic.Localized en / zh / zht copy with parity tests; the other 14 locales fall back to en by design.
Why
The original trigger bug (#1123): a DeepSeek account in arrears returns
402 Insufficient Balance, but the UI overwrote it with a generic "Connection lost", giving the user no idea their balance was the cause or what to do. PR1 classified the failure (quota_exhausted), PR2 stopped masking the reason, PR3 decoded the structured reason. This PR is the render half: turn that signal into plain guidance plus the real reason on demand.Related Issue
Thread A of the error-experience refactor (#1105 / #1123). This is PR4 (render), following the merged PR1-PR3 (classify + decode).
Human Review Status
Pending
Review Focus
error-card.ts: the kind → presentation mapping and the unknown / missing-kind fallback (anything unrecognized degrades tounknownraw-body).error-card.tsx: therawBodybranch in thebody()/detail()memos.packages/uifree of app imports.data-slotwiring the E2E and snap selectors depend on.unknownraw-reason body is height-capped (.error-card__raw, 240px + scroll) so an unbounded provider error can't stretch the timeline open; known kinds keep their short copy uncapped.Risk Notes
Low. Render-only change on an already-classified signal; no new dependencies, no backend or platform surface touched.
The 14 non-en/zh/zht locales fall back to en for the new copy. This is the established pattern in this codebase (locales ship intentionally partial; the app-level parity test itself only checks a curated zh subset), not a regression.
Skipped conditional checklist items:
How To Verify
The E2E proves the full original-bug fix end to end: 402 → per-kind card → title "余额不足" → body is the plain copy and does NOT contain the raw reason → detail discloses "Insufficient Balance" → primary action lands on the models settings tab (
aria-selected="true").Screenshots or Recordings
Visual check: the
error-cardsnap target renders thequota_exhaustedcard collapsed then detail-expanded, in both locales the app renders at runtime — zh and en (the base every untranslated locale falls back to). Reviewed the 2x2 grid atdocs/design/preview/screenshots/error-card.png: red 2px rule, circle-ban icon, localized title ("余额不足" / "Out of balance"), plain body, orange primary button ("换个模型" / "Switch model"), and a "详情" / "Details" disclosure that expands to the provider reason "Insufficient Balance" in a cream/mono box.Checklist
bug,enhancement,task,documentation.app,ui,platform,harness,ci. The labeler bot assignedapp+ui, matching the changed paths.P0,P1,P2,P3. The priority-triage bot suggestedP2, confirmed.Pending,Approved by @<reviewer>, orNot required: <reason>(default isPending; "not required" is restricted to bot-authored low-risk PRs).dev, and my PR title and commit messages use Conventional Commits in English.