Error
One way to show a server error (4xx / 5xx). Photon services answer failures
with an RFC 9457 problem body —
code, title, detail, remediation, requestId — and these components
render that contract directly: hand them the caught value and the reviewed
server copy appears; anything else falls back to a generic line instead of
leaking a raw message.
Two presentations, one per altitude. Error is the inline line for forms,
cards, and action rows; ErrorView is the failed state of a region or page,
built on Status View. Both render nothing while
the error is nullish, so they can sit unconditionally next to a query or
mutation.
Client-side validation is not this component’s job — Field.Error handles
messages wired to control validity.
Import
import { Error, ErrorView } from "@photon-ai/pho-ui/components/error";
Inline
Place Error where the failure happened. It announces via role="alert",
shows the problem’s detail and remediation sentence, and links the
problem’s documentation when it carries one.
const { mutate, error } = useMutation(…);
<Error error={error} />
Details
When the caught value carried a problem (or at least a request id), the line
ends with an info glyph. Hovering or focusing it opens the request’s fuller
story: the problem title, the untruncated description, then status /
code and the copyable request id grouped below for a support handoff. A
generic unknown has nothing more to disclose, so the glyph doesn’t render.
The panel is a Popover using the default Title / Description styles
(its copy button is interactive), opened on hover so the gesture stays
tooltip-light.
// Nothing to wire — the disclosure derives from the same `error` value.
<Error error={error} />
Fallback
When the response was not ok but the body wasn’t a contract problem, the
client still knows the HTTP status — title becomes the reason phrase
(e.g. “Bad Gateway”), the description explains it in a sentence, and the
details panel also shows the numeric status. A true unknown (network
failure, unexpected exception) stays the generic line with nothing more to
disclose. Raw Error messages are never shown; pass children to override
the copy.
// HTTP status known, no problem body → reason phrase + status in details
<Error error={apiError} />
// No status at all → generic line only
<Error error={new TypeError("ECONNREFUSED 10.0.0.1:443")} />
<Error error={error}>Couldn't save your changes.</Error>
Error view
For a region or page that failed to load — Status View
with the error wiring done: warning icon, role="alert", title and
description from the problem. onRetry renders a “Try again” button, the
problem’s documentation renders a “Learn more” link, and the request id sits
below as a footnote for support.
const { data, error, refetch } = useQuery(…);
if (error) {
return <ErrorView error={error} onRetry={() => refetch()} />;
}
Sizes
Status View’s axis, passed through — sm for cards and panels, the default
base for whole pages.
<ErrorView size="sm" error={error} onRetry={() => refetch()} />
<ErrorView size="base" error={error} onRetry={() => refetch()} />
Actions
actions replaces the built-in row when the way out isn’t a retry — a route
back, a reload, an upgrade.
<ErrorView
error={error}
actions={
<>
<Button size="sm" onClick={() => window.location.reload()}>
Reload
</Button>
<Button
size="sm"
variant="outlined"
onClick={() => window.history.back()}
>
Back
</Button>
</>
}
/>
Parsing
The contract helpers are exported for surfaces beyond these two — branching
on code, or feeding a toast. parseProblem finds a problem carried by the
value itself, typed onto an error as a problem field (an ApiError
shape), or along an Error cause chain; describeError turns any caught
value into displayable copy.
import {
describeError,
parseProblem,
type Problem,
} from "@photon-ai/pho-ui/components/error";
const problem = parseProblem(error);
if (problem?.code === "NOT_AUTHENTICATED") {
redirectToSignIn();
}
const { title, description } = describeError(error);
toast.error(title, { description });
Guidelines
- Branch on
code, never ontitleordetail— the copy can change, the code is the contract. - Let the server speak: the problem’s
title/detail/remediationare reviewed copy. Override withchildren(inline) ortitle/description(view) only when the surrounding UI carries the context. - Keep the request id visible on page-level errors — it is how support correlates a report with the logs.
- Use
Errorfor a failure scoped to an action the user just took;ErrorViewwhen a region has nothing to show because loading failed; a toast (viadescribeError) when the user may have navigated away. - Validation issues (
422) belong next to their fields — map the problem’sissuesextension onto the form; the form-levelErrorthen only carries the summary sentence.