Skip to content
tRPC

Error Handling

On this page

A failed procedure call rejects with a TRPCClientError. You can either let it throw and catch it, or use safe() to get it back as a type-safe value.

Catching a thrown error

query() and mutate() behave like any other promise, so a try/catch works as you would expect. TypeScript has no way to describe what a function throws, though, so cause arrives as unknown and has to be narrowed:

client.ts
import { TRPCClientError } from '@trpc/client';
import type { AppRouter } from './server';
import { trpc } from './trpc';

try {
  const post = await trpc.post.byId.query('1');
} catch (cause) {
  if (cause instanceof TRPCClientError) {
    console.log(cause.data);
  } else {
    // something that isn't a tRPC error
  }
}

See Inferring Types for a reusable isTRPCClientError guard.

Errors as values with safe()

safe() awaits a query() or mutate() call without letting it throw, handing back a [data, error] pair instead. Exactly one side is ever set, so checking either one narrows the other:

client.ts
import { safe } from '@trpc/client';
import { trpc } from './trpc';

const [post, error] = await safe(trpc.post.byId.query('1'));

if (error) {
  console.log(error.data);
} else {
  console.log(post.title);
}