Error Handling
When a Rust function returns Result<T, E>, errors are propagated as RpcError on the client side. You can also return custom HTTP status codes for authorization
or validation errors.
The RpcError class provides .status, .message, and .data for structured error handling.
RpcError API
| Property | Type | Description |
|---|---|---|
.status | number | HTTP status code (e.g. 401, 500) |
.message | string | Human-readable error message |
.data | unknown | Parsed JSON error body from server |
Global onError Hook
Catch all errors at the client level. The onError callback receives an ErrorContext with the procedure name, attempt number, and whether the client will retry.
// Global error handler via RpcClientConfig
const rpc = createRpcClient({
baseUrl: '/api',
onError: (ctx) => {
console.error(`${ctx.procedure} failed (attempt ${ctx.attempt})`);
if (ctx.willRetry) {
console.log('Retrying...');
}
},
});Reactive Error Handling
Framework wrappers expose isError and error state for conditional UI rendering.
// Reactive error handling with createQuery / createMutation
const users = createQuery(rpc, 'list_users', () => page, {
onError: (err) => {
// err is RpcError
if (err.status === 401) {
redirectToLogin();
}
},
});
// Check error state in templates
// users.isError — boolean
// users.error — RpcError | undefinedMutation Errors
Use mutateAsync with try/catch for fine-grained control, or onError callback for fire-and-forget style.
// Mutation error handling with mutateAsync
const create = createMutation(rpc, 'create_order');
try {
const order = await create.mutateAsync(orderInput);
} catch (e) {
if (e instanceof RpcError) {
switch (e.status) {
case 400: showValidationErrors(e.data); break;
case 409: showConflictMessage(); break;
default: showGenericError(e.message);
}
}
}
// Or fire-and-forget with callback
const create2 = createMutation(rpc, 'create_order', {
onError: (err) => toast.error(err.message),
});
create2.mutate(orderInput);Timeout & Abort
Timeouts and manual aborts throw a DOMException with name: 'AbortError', not RpcError.
// Timeout and abort errors
const controller = new AbortController();
try {
await rpc.query('slow_report', input, {
timeout: 5000,
signal: controller.signal,
});
} catch (e) {
if (e instanceof RpcError) {
// Server returned an error status
} else if (e instanceof DOMException && e.name === 'AbortError') {
// Request was aborted (timeout or manual cancel)
}
}Secret — Protected Endpoint
This endpoint requires an Authorization header. Try calling it with and without a token
to see error handling in action.