Error Handling
Generated clients return declared error responses as part of the normal response union, while infrastructure problems are thrown as exceptions.
- contract-level error responses that are part of the operation response union
- 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:
NetworkErrorResponseParseErrorPathParameterErrorUnknownResponseError
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
catchonly 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:
| Error | Useful fields | When you usually use them |
|---|---|---|
NetworkError | code, method, url | log transport failures, decide whether retry UI makes sense |
ResponseParseError | statusCode, bodyPreview | inspect malformed JSON or unexpected payload bodies |
PathParameterError | paramName, path | catch bad command construction before the request is sent |
UnknownResponseError | statusCode, header, body, validationError | inspect 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.