Error Handling

Generated clients return declared error responses as part of the normal response union, while infrastructure problems are thrown as exceptions.

  1. contract-level error responses that are part of the operation response union
  2. infrastructure-level failures that are thrown as exceptions

That distinction keeps client code predictable.

Contract-level errors are normal responses

If an operation declares a not-found or validation error response, the generated client returns that as part of the normal response union.

That means you handle it with response.type, not with catch.

Infrastructure failures are thrown

Generated clients can also throw errors for problems outside the contract, such as:

  • NetworkError
  • ResponseParseError
  • PathParameterError
  • UnknownResponseError

These mean the client could not even produce a valid contract response value.

A practical handling pattern

import {
  GetTodoRequestCommand,
  NetworkError,
  ResponseParseError,
  TodoClient,
} from "./api/generated";
import { UnknownResponseError } from "@rexeus/typeweaver-core";

const client = new TodoClient({
  baseUrl: "/api",
  timeoutMs: 5_000,
});

async function loadTodo(todoId: string) {
  try {
    const response = await client.send(
      new GetTodoRequestCommand({
        param: {
          todoId,
        },
      }),
    );

    switch (response.type) {
      case "GetTodoSuccess":
        return {
          kind: "success" as const,
          todo: response.body,
        };
      case "TodoNotFoundError":
        return {
          kind: "not-found" as const,
        };
    }
  } catch (error) {
    if (error instanceof NetworkError) {
      return {
        kind: "retryable-failure" as const,
        message: "Check your connection and try again.",
      };
    }

    if (
      error instanceof ResponseParseError ||
      error instanceof UnknownResponseError
    ) {
      throw error;
    }

    throw error;
  }
}

The important idea is:

  • use switch (response.type) for declared API outcomes such as not found
  • use catch only for failures outside the contract, such as network issues or invalid server responses

Practical error fields

These thrown errors carry a few fields that are useful in logs and user-facing fallback handling:

ErrorUseful fieldsWhen you usually use them
NetworkErrorcode, method, urllog transport failures, decide whether retry UI makes sense
ResponseParseErrorstatusCode, bodyPreviewinspect malformed JSON or unexpected payload bodies
PathParameterErrorparamName, pathcatch bad command construction before the request is sent
UnknownResponseErrorstatusCode, header, body, validationErrorinspect undeclared or invalid responses from the server

Treat these as debugging and fallback details. Declared API responses should still be handled through response.type.

Why this matters

If you put everything in catch, you lose the benefits of explicit error responses in the contract. If you treat transport failures as normal responses, you blur a real boundary.

Keep those two layers separate and generated clients stay easy to use.

Was this page helpful?