Error Handling
Surface useful safe errors to users and correlate them with trusted server diagnostics.
PAI separates errors by audience:
| Audience | Receives |
|---|---|
| Application UI and clients | A stable safe code, safe message, optional retry guidance, and optional diagnostic reference |
| Persisted thread and run state | The same client-safe error record |
| Trusted server reporter | The original exception plus safe operational correlation fields |
| Telemetry | Safe error and correlation fields by default; raw details only under an explicit trusted policy |
This lets an application show a useful error:
The model service is temporarily unavailable. Please try again.
Reference: PAI-7e57dfc9-...Support can search its logger, Sentry project, or telemetry backend for
PAI-7e57dfc9-... and find the corresponding server diagnostic. The reference is
opaque and does not grant access to an error record.
Reporting is best-effort. PAI submits the matching event before returning the reference, but the application remains responsible for sink delivery, retention, and monitoring.
Configure The Server
Use the runtime mapError option and receiver mapHttpError option to turn
recognized application failures into safe details. Unexpected errors use PAI's
generic safe fallback.
import { createPai } from "@pai/core";
import type { PaiErrorDetails } from "@pai/protocol";
import { createPaiHonoReceiver } from "@pai/hono";
function mapError(error: unknown): PaiErrorDetails | undefined {
if (error instanceof ModelRateLimitError) {
return {
code: "model_rate_limited",
message: "The service is busy. Please try again shortly.",
retryable: true,
};
}
return undefined;
}
const pai = createPai({
agents: { main: assistantAgent },
scopeKey: (identity) => identity.workspaceId,
mapError,
onRuntimeError(event) {
logger.error(
{
errorId: "errorId" in event ? event.errorId : undefined,
event,
},
"PAI runtime failure",
);
},
});
export const routes = createPaiHonoReceiver({
pai,
resolveIdentity,
mapHttpError: mapError,
onHttpError(event) {
logger.error(
{
errorId: event.errorId,
phase: event.phase,
operation: event.operation,
threadId: event.threadId,
runId: event.runId,
err: event.error,
},
"PAI HTTP failure",
);
},
});The runtime mapError mapper covers run and tool failures projected into
persisted state. The receiver mapHttpError mapper covers unexpected failures
at the HTTP boundary, including failures that happen before runtime execution.
A shared pure function is convenient when both boundaries use the same
application error classes.
The two reporters have different ownership:
onHttpErrorreports a request or established HTTP stream that the receiver cannot complete.onRuntimeErrorreports background or best-effort runtime work after no initiating caller remains available to receive the rejection, plus local backend toolexecuteexceptions that PAI converts into safe tool results.
The same failure is not normally reported through both hooks.
Controlled tool outcomes — including ctx.fail(), lifecycle rejection,
cancellation, missing or disabled tools, pending-action resume outcomes, and
provider-emitted tool errors — are not runtime incidents, and their messages are
not redacted. An author wrote them for the model to read, so the model and the
client receive them as written. Redaction is for thrown values.
Render Client Errors
A background run failure is durable thread state:
function ChatFailure() {
const chat = AssistantAI.useChat();
const error = chat.state.error;
if (!error) return null;
return (
<aside role="alert">
<p>{error.message}</p>
{error.errorId ? <small>Reference: {error.errorId}</small> : null}
{error.retryable ? <button onClick={() => void chat.retry()}>Retry</button> : null}
</aside>
);
}chat.state.error is the latest durable run failure. chat.error is a separate
local command or observation error caught by the React controller; when it came
from the HTTP transport it may be a PaiHttpError.
See Client Errors for both paths.
Support Workflow
- Show the safe message and optional reference in the UI.
- Ask the user to copy the reference or include it in a screenshot.
- Search the configured server sink for the same
errorId. - Use the event's operation, phase, agent, thread, and run correlation to reconstruct the failure.
- Inspect original exception details only within the application's approved data-access and retention policy.
PAI propagates the reference; it does not host an error lookup endpoint or own your diagnostic retention.
Next
- Client Errors: typed errors, HTTP fallback errors, durable run failures, and React rendering.
- Server Diagnostics: reporter boundaries, phases, and logger integration.
- Privacy And Redaction: safe projections, raw exception handling, and telemetry policy.
- Observability: OpenTelemetry traces for runs, models, and tools.