PAIPAI
Error Handling

Error Handling

Surface useful safe errors to users and correlate them with trusted server diagnostics.

PAI separates errors by audience:

AudienceReceives
Application UI and clientsA stable safe code, safe message, optional retry guidance, and optional diagnostic reference
Persisted thread and run stateThe same client-safe error record
Trusted server reporterThe original exception plus safe operational correlation fields
TelemetrySafe 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:

  • onHttpError reports a request or established HTTP stream that the receiver cannot complete.
  • onRuntimeError reports background or best-effort runtime work after no initiating caller remains available to receive the rejection, plus local backend tool execute exceptions 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

  1. Show the safe message and optional reference in the UI.
  2. Ask the user to copy the reference or include it in a screenshot.
  3. Search the configured server sink for the same errorId.
  4. Use the event's operation, phase, agent, thread, and run correlation to reconstruct the failure.
  5. 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

On this page