Error Handling in oRPC Clients 
This guide explains how to handle type-safe errors in oRPC clients using type-safe error handling. Both server-side and client-side clients are supported.
Using safe and isDefinedError 
ts
import { isDefinedError, safe } from '@orpc/client'
const doSomething = os
  .input(z.object({ id: z.string() }))
  .errors({
    RATE_LIMIT_EXCEEDED: {
      data: z.object({ retryAfter: z.number() })
    }
  })
  .handler(async ({ input, errors }) => {
    throw errors.RATE_LIMIT_EXCEEDED({ data: { retryAfter: 1000 } })
    return { id: input.id }
  })
  .callable()
const [error, data, isDefined] = await safe(doSomething({ id: '123' }))
// or const { error, data, isDefined } = await safe(doSomething({ id: '123' }))
if (isDefinedError(error)) { // or isDefined
  // handle known error
  console.log(error.data.retryAfter)
}
else if (error) {
  // handle unknown error
}
else {
  // handle success
  console.log(data)
}INFO
- safeworks like- try/catch, but can infer error types.
- safesupports both tuple- [error, data, isDefined]and object- { error, data, isDefined }styles.
- isDefinedErrorchecks if an error originates from- .errors.
- isDefinedcan replace- isDefinedError
Safe Client 
If you often use safe for error handling, createSafeClient can simplify your code by automatically wrapping all procedure calls with safe. It works with both server-side and client-side clients.
ts
import { createSafeClient } from '@orpc/client'
const safeClient = createSafeClient(client)
const [error, data] = await safeClient.doSomething({ id: '123' })