Skip to Content
GuidesError Handling

Error Handling

Error types

ErrorPackageThrown when
ValidationError@routar/coreRequest or response validation fails
HttpError@routar/coreServer returns a non-2xx status (fetch executor only)
TimeoutError@routar/coreRequest exceeds the createFetchExecutor timeout
AxiosErroraxiosNetwork or HTTP error via Axios

Handling errors

import { TimeoutError, ValidationError } from '@routar/core' import { HttpError } from '@routar/core' try { await todoApi.create({ body: { title: '' } }) } catch (err) { if (err instanceof ValidationError) { // request or response schema failed console.log(err.message) console.log(err.cause) // original Zod/Valibot error } if (err instanceof HttpError) { // fetch returned non-2xx console.log(err.status) // e.g. 422 console.log(err.statusText) // e.g. 'Unprocessable Entity' console.log(err.body) // parsed JSON payload, or null } if (err instanceof TimeoutError) { console.log(err.ms) // configured timeout in ms } }

Branching by executor type

Each transport throws a different error type on HTTP failures. If you’re switching executors or want to handle all HTTP errors in one place:

import { HttpError } from '@routar/core' import { isAxiosError } from 'axios' import { HTTPError } from 'ky' try { await todoApi.getDetail({ path: { id: 1 } }) } catch (err) { if (err instanceof HttpError) { // createFetchExecutor — non-2xx response console.log(err.status, err.body) } else if (isAxiosError(err)) { // @routar/axios — Axios network or HTTP error console.log(err.response?.status, err.response?.data) } else if (err instanceof HTTPError) { // @routar/ky — ky HTTP error console.log(err.response.status, await err.response.json()) } }

Normalizing errors across transports

Each transport throws its own error type. If you want a single instanceof check across fetch and axios, add a normalizer plugin with an onError hook:

import { definePlugin, HttpError } from '@routar/core' import { isAxiosError } from 'axios' const errorNormalizer = definePlugin({ name: 'error-normalizer', onError: async (err) => { if (isAxiosError(err)) { throw new HttpError(err.response?.status ?? 0, err.message) } throw err }, }) const executor = createAxiosExecutor(instance, { plugins: [errorNormalizer], })

Retry only on server errors

retry is an option on createFetchExecutor:

import { createFetchExecutor, HttpError } from '@routar/core' createFetchExecutor('https://api.example.com', { retry: { count: 3, shouldRetry: (err) => { if (err instanceof HttpError) return err.status >= 500 return true // retry on network errors }, }, })
Last updated on