Skip to content

feat(error): decode the provider's real reason from the structured payload#1469

Open
Astro-Han wants to merge 1 commit into
devfrom
claude/i1105-pr3-frontend-decode
Open

feat(error): decode the provider's real reason from the structured payload#1469
Astro-Han wants to merge 1 commit into
devfrom
claude/i1105-pr3-frontend-decode

Conversation

@Astro-Han

Copy link
Copy Markdown
Owner

Summary

Add one shared frontend decoder — @opencode-ai/ui/util/server-error's decodeServerErrorText — that turns a server / assistant error payload into the best plain text already present on it: it reads the structured response body (real JSON, via JSON.parse) for the provider's real reason, then falls back to the already-extracted message. Both the session-turn error card and the app toast formatter (formatServerError) now route through it, and the old per-site brace-hunting (unwrap/record in session-turn.tsx) is deleted.

This is the frontend decode foundation of the classify → passthrough → render chain. Per-kind copy and card visuals are the design-gated follow-up and are intentionally not here — the decoder invents no copy.

Why

A DeepSeek direct 402 ("Insufficient Balance") was shown to users as a generic message because two display sites each sniffed JSON out of a display string with their own brittle brace-hunting, losing the provider's real reason. Consolidating to one structured decoder lets the UI surface the real reason once the backend passes it through (the classification + passthrough land in the sibling PRs of this chain). It also removes a duplicated, fragile parsing path.

The provider's reason is passthrough, not localized copy. The i18n parity test is upgraded accordingly: instead of only checking zh-vs-en key alignment, it now asserts a provider error renders verbatim in every locale (never mapped to a translation key).

Related Issue

Part of #1123 (classify-then-render umbrella); classification spine #1105. This PR is the frontend render piece; the backend classification/passthrough are the sibling PRs (#1466, #1468).

Human Review Status

Pending

Review Focus

  • decodeServerErrorText decode order: structured responseBodyfromStructuredBody(message) → raw messageundefined. Confirm a clean message still passes through verbatim and a non-payload value returns undefined so callers keep their own fallback.
  • session-turn.tsx: the errorText memo now delegates to the decoder; unwrap/record are removed. The String(msg) branch is only reachable for a non-string message (the decoder already handles strings).
  • formatServerError: the decoder is consulted after the typed-error checks (ConfigInvalid / ProviderModelNotFound) and before the Error/string fallbacks, so typed errors keep their tailored copy.

Risk Notes

  • Design-neutral plumbing: no new per-kind copy or card visuals (those are the design-gated PR4). The error card chrome is unchanged; only the text it receives changes.
  • End-to-end effect (real reason instead of "Connection lost") is only observable once the backend passthrough merges; this PR is flat from dev and does not include it. The decoder is therefore exercised by unit tests and a fixture-mounted snap with synthetic post-passthrough payloads.
  • Platform conditional left unticked: pure web rendering (text in an existing Card); no Electron/IPC/platform/packaging surface touched.
  • Docs/deps/credentials conditional left unticked: none of those surfaces touched (the snap PNG is a local-only artifact under docs/, not committed).

How To Verify

bun test packages/ui server-error.test.ts: 6 pass
bun test packages/app parity.test.ts + server-errors.test.ts: 11 pass
tsgo --noEmit (packages/ui): clean
tsgo --noEmit (packages/app): clean
eslint changed src files: 0 errors
snap error-card-decode: 1 passed; grid reviewed — decoded reasons (incl. a
  "type:" prefix and a long multi-line reason) wrap cleanly in the real
  Card variant="error", no overflow; generic message still renders when the
  payload carries no structured reason
codex review --base origin/dev (xhigh): no actionable regressions

Screenshots or Recordings

Visible surface: the session-turn error card (Card variant="error" class="error-card"). Checked via the new focused snap target error-card-decode (packages/app/e2e/snap/error-card-decode.snap.ts + fixture), which mounts the real Card + real decoder over five representative payloads. Grid reviewed locally at docs/design/preview/screenshots/error-card-decode.png (local-only artifact): the 402 structured body card shows unknown_error: Insufficient Balance, json-in-message shows rate limited (no JSON blob), and the long reason wraps across three lines with the error accent bar intact.

Checklist

  • Type label — this PR carries exactly one of bug, enhancement, task, documentation.
  • Routing labels — this PR carries at least one of app, ui, platform, harness, ci.
  • Priority label — this PR carries exactly one of P0, P1, P2, P3.
  • Human Review Status above is set to Pending, Approved by @<reviewer>, or Not required: <reason>.
  • I linked the related issue, or stated in Summary why there is no issue.
  • I described the review focus and any meaningful risks.
  • I replaced the example block in How To Verify with the real verification steps and the key result for each.
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope.
  • (conditional) I manually checked visible UI or copy changes when needed, with screenshots or recordings.
  • (conditional) I considered macOS and Windows impact for platform, packaging, updater, signing, paths, shell, or permissions changes. Leave unticked only if no platform/packaging surface was touched.
  • (conditional) I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant. Leave unticked only if none of those surfaces was touched.
  • I reviewed the final diff for unrelated changes and suspicious dependency changes.
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English.

https://claude.ai/code/session_015bW9JQSkuB156gkNQdxCzi

…yload

The session error card and the toast formatter each sniffed JSON out of a
display string with their own brace-hunting (`unwrap` in session-turn,
ad-hoc parsing in `formatServerError`). That loses the provider's real
reason: a DeepSeek 402 surfaced as a generic message instead of
"Insufficient Balance".

Add one shared decoder, `@opencode-ai/ui/util/server-error`'s
`decodeServerErrorText`, that reads the structured response body (real
JSON, via `JSON.parse` — no brace-hunting) and falls back to the already
-extracted message. Route both the session-turn error card and the app
toast formatter through it, and delete the old `unwrap`/`record` helpers.

This is the frontend decode foundation. Per-kind copy and card visuals are
the design-gated follow-up and are intentionally not here — the decoder
turns a payload into the best plain text already present, inventing none.

The provider's reason is passthrough, not localized: the i18n parity test
now asserts a provider error renders verbatim in every locale (not mapped
to a translation key) rather than only checking zh-vs-en key alignment.

Verification:
- bun test: ui server-error (6) + app parity/server-errors (11) green
- tsgo --noEmit clean in packages/ui and packages/app
- eslint clean on changed src files
- new focused snap target `error-card-decode`: real `Card variant="error"`
  + real decoder over representative payloads; grid reviewed — decoded
  reasons (incl. a `type:` prefix and a long multi-line reason) wrap
  cleanly in the error card, no overflow

Claude-Session: https://claude.ai/code/session_015bW9JQSkuB156gkNQdxCzi
@Astro-Han Astro-Han added enhancement New feature or request P2 Medium priority labels Jun 22, 2026
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@Astro-Han, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 32 minutes and 35 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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a638b85-d78d-472a-ad5e-fa14bb13036b

📥 Commits

Reviewing files that changed from the base of the PR and between f433411 and 3062c27.

📒 Files selected for processing (7)
  • packages/app/e2e/snap/error-card-decode.snap.ts
  • packages/app/e2e/snap/fixtures/error-card-decode-snap-fixture.tsx
  • packages/app/src/i18n/parity.test.ts
  • packages/app/src/utils/server-errors.ts
  • packages/ui/src/components/session-turn.tsx
  • packages/ui/src/util/server-error.test.ts
  • packages/ui/src/util/server-error.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/i1105-pr3-frontend-decode

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 and usage tips.

@github-actions github-actions Bot added app Application behavior and product flows ui Design system and user interface labels Jun 22, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested priority: P2 (includes user-path files (packages/app/src/i18n/parity.test.ts, packages/app/src/utils/server-errors.ts)).

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.

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

Labels

app Application behavior and product flows enhancement New feature or request P2 Medium priority ui Design system and user interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant