Skip to content

fix(errors): surface Harper 5 RFC 9457 problem details in error toasts - #1598

Merged
Devin-Holland merged 1 commit into
stagefrom
fix/rfc9457-error-toasts
Aug 7, 2026
Merged

fix(errors): surface Harper 5 RFC 9457 problem details in error toasts#1598
Devin-Holland merged 1 commit into
stagefrom
fix/rfc9457-error-toasts

Conversation

@Devin-Holland

Copy link
Copy Markdown
Member

What

Harper 5 changed REST error responses from "Code: message" strings to RFC 9457 Problem Details objects:

{ "type": "error:ClientError", "code": "ClientError", "title": "Not allowed", "status": 403, "instance": "/Cluster/clu-123" }

The global errorHandler only looked at data.error / data.message, so against a Harper 5 central-manager every error toast fell through to the generic "We had some trouble!".

Changes

  • queryClient.ts — recognize a Problem Details body: code → toast title, title (+ detail when present) → description. The title is a plain sentence that can itself contain colons ("Plan not found: plan-123"), so the legacy "Title: detail" colon-split is skipped for this shape. v4 string/error/message handling is unchanged, so stage/prod CMs still on 4.x are unaffected.
  • isEmailNotVerifiedError.ts — same class of bug found by sweeping for other CM error-body parsing: the unverified-email login detection only read error/message, so on a v5 CM an unverified user would get a dead-end toast instead of the email-verification redirect. Now also checks title.

Verification

  • 6 new unit tests (4 errorHandler RFC 9457 cases incl. colon-in-title and detail-append; 2 for the unverified-email matcher) — 25/25 pass across both files, full suite green via the pre-commit hook (2151 passed / 11 skipped).
  • Field mapping verified against Harper 5 source (server/REST.ts Problem Details construction): code = error.code ?? constructor.name, title = error.message, detail only present when set.

— devain (Claude)

@Devin-Holland
Devin-Holland requested a review from a team as a code owner August 5, 2026 14:31

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request adds support for RFC 9457 Problem Details error formats introduced in Harper 5. Specifically, it updates 'isEmailNotVerifiedError' to check the 'title' field in addition to 'error' and 'message', and enhances the global 'errorHandler' in 'queryClient.ts' to map 'code' to the toast title and 'title' (plus optional 'detail') to the description, while preventing incorrect colon-splitting. Comprehensive unit tests have been added to verify these new error-handling behaviors. I have no additional feedback to provide as the implementation is clean and well-tested.

Harper 5 REST errors changed from '$Code: $message' strings to RFC 9457
Problem Details objects ({ type, code, title, status, detail, instance }),
so against a v5 central-manager every error toast fell through to the
generic 'We had some trouble!'. Map code -> toast title and
title (+ detail) -> description, without colon-splitting the title.

Same class: isEmailNotVerifiedError only read error/message, so on v5
the unverified-email login rejection lost its verification redirect —
now also checks title.
@Devin-Holland
Devin-Holland force-pushed the fix/rfc9457-error-toasts branch from 48c2831 to 1a072f0 Compare August 5, 2026 14:35
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 54.28% 6434 / 11853
🔵 Statements 54.91% 6929 / 12618
🔵 Functions 45.88% 1560 / 3400
🔵 Branches 48% 4431 / 9231
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/features/auth/isEmailNotVerifiedError.ts 100% 100% 100% 100%
src/react-query/queryClient.ts 96.87% 94.28% 66.66% 96.87% 61
Generated in workflow #1668 for commit 1a072f0 by the Vitest Coverage Report Action

@DavidCockerill DavidCockerill left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approving — nothing to change.

The mapping is deliberately non-literal and that's the right call. Harper's REST.ts builds code = error.code ?? error.constructor.name and title = error.message, so code → heading / title (+ detail) → description produces a sensible toast; an RFC-purist mapping would have put a full sentence in the heading and left the body empty. The splitTitleFromMsg = false suppression is the subtle part and it's correct — without it "Plan not found: plan-123" gets sliced at the colon, and you pinned exactly that case.

I traced 23 error shapes through the new handler rather than just the happy ones — v5 problem details (including a numeric code and an empty title), v5 ops-API {error}, v4 string bodies, an nginx HTML 502, network/CORS/timeout, undefined/null/{}/array/Blob, a non-string title, both-title-and-error, and a circular detail. No blank toast, no [object Object], no raw JSON blob, and nothing throws inside the handler on any of them (errorText catches circular JSON, data.title is narrowed to string before the template literal, and errorMsg is provably a string at the .includes(':') guard on every path).

Two things I specifically went looking for and didn't find:

  • The verification gate still matches. This was the real risk — a detection that quietly stops matching means unverified users get a generic error instead of the resend flow. The change is purely additive (title appended last, existing fields win), and I checked the live wire format rather than the intent: central-manager pins harperdb: ^4.7.29, so deployed CM serializes errorToString(error) and the rejection arrives as a string, taking the untouched branch entirely. On v5 the new branch matches the same text. No fail-open either — of the three 403s reachable from POST /Login, only the intended one matches /verif/i.
  • No new sensitive-data exposure. instance (the request URL, the one field that could carry a token in a query string) and type are deliberately not surfaced, and the logging path is unchanged.

One thing worth knowing but explicitly not yours to fix here: a proxy returning text/html still renders the whole document — tags and nginx version banner — into the toast, via the typeof data === 'string' branch this PR only reindents. It's now the last error shape without sane handling. Happy to file that separately if you want it tracked.

Reviewed by Claude Opus 5 for @DavidCockerill.

@Devin-Holland
Devin-Holland merged commit 983e9d5 into stage Aug 7, 2026
2 checks passed
@Devin-Holland
Devin-Holland deleted the fix/rfc9457-error-toasts branch August 7, 2026 13:17
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.

3 participants