How-to guides

Error handling

Handle failures from the client Kubb generates. Catch a ResponseError, switch to a returned error result with throwOnError, and tell a network failure apart from a 4xx or 5xx response.

Generated Fetch and Axios clients throw on non-2xx responses by default. Set the generated default on the client plugin. Individual operations can override it:

import { getPetById } from './gen/clients/getPetById'
import { ResponseError } from './gen/.kubb/client'

try {
  const { data } = await getPetById({ path: { petId: 1 } })
  console.info(data.name)
} catch (error) {
  if (ResponseError.is(error)) {
    console.error(error.status) // 404
    console.error(error.data) // the parsed error body
  }
}

A ResponseError includes the HTTP method and URL (without query parameters, keeping credentials safe) in its message, e.g. GET https://api.example.com/pets/1 failed with status 404 Not Found.

The error exposes these fields:

class ResponseError extends Error {
  static is(error: unknown): error is ResponseError<unknown, unknown, unknown>
  data: TError // the parsed error body
  status: number
  statusText: string
  contentType: string | undefined
  request: Request // AxiosRequestConfig on plugin-axios
  response: Response // AxiosResponse on plugin-axios
}

Use ResponseError.is(error) to narrow errors across generated packages, which each bundle their own class. It checks the error name.

Return the error instead

Pass throwOnError: false and the call resolves for every documented status. The result is a discriminated union: data is set on success and error on failure, so branch on status or check which field is present.

const result = await getPetById({ path: { petId: 1 }, throwOnError: false })

if (result.error) {
  console.error(result.status, result.error)
} else {
  console.info(result.data.name)
}

Check status to narrow responses to a specific documented status code.

Set the generated default

Set throwOnErrorDefault on @kubb/plugin-fetch or @kubb/plugin-axios to choose how generated operations handle non-2xx responses by default. Each call can still override that setting with throwOnError:

import { pluginFetch } from '@kubb/plugin-fetch'

pluginFetch({ throwOnErrorDefault: false })

// resolves with an error result
const list = await searchPets({ query: { status: 'available' } })

// opt one call back into throwing
const pet = await getPetById({ path: { petId: 1 }, throwOnError: true })

The generated operation passes its selected value to the client runtime, so setting throwOnError with client.setConfig does not change this default. Configure the plugin to change the generated default.

Network failures still throw

throwOnError governs the response status, not the send: a dropped connection, DNS failure, or aborted AbortSignal rejects regardless of the setting. A ResponseError means the server answered with a non-2xx, and anything else means the request never completed.

try {
  const { data } = await getPetById({ path: { petId: 1 }, throwOnError: false })
  use(data)
} catch (error) {
  // not a ResponseError: the request never completed
  console.error('request failed to send', error)
}

Pass an AbortSignal to cancel a request. The call rejects with the abort reason.

Validation failures

When you turn on the validator option, a body that does not match its schema throws a ParseError instead of returning. It carries the raw issues from the schema, so the same handling works across Zod, valibot, and arktype:

import { ParseError } from './gen/.kubb/standardSchema'

try {
  const { data } = await getPetById({ path: { petId: 1 } })
  use(data)
} catch (error) {
  if (error instanceof ParseError) {
    console.error(error.issues) // [{ message, path }]
  }
}

A ParseError reports schema validation issues. A ResponseError reports a non-2xx status. On the non-throwing path, configured error schemas validate the error body separately from success schemas.

Calling .unwrap() on a throwOnError: false call turns that same error into a rejection, so a try/catch works there too. See unwrap the success body.

See also