Error Formatting
The error formatting in your router will be inferred all the way to your client.
Usage example highlighted
Adding custom formatting
server.tstsimport {initTRPC } from '@trpc/server';import {ZodError } from 'zod';export constt =initTRPC .create ({errorFormatter (opts ) {const {shape ,error } =opts ;return {...shape ,data : {...shape .data ,zodError :error .code === 'BAD_REQUEST' &&error .cause instanceofZodError ?error .cause .flatten (): null,},};},});
server.tstsimport {initTRPC } from '@trpc/server';import {ZodError } from 'zod';export constt =initTRPC .create ({errorFormatter (opts ) {const {shape ,error } =opts ;return {...shape ,data : {...shape .data ,zodError :error .code === 'BAD_REQUEST' &&error .cause instanceofZodError ?error .cause .flatten (): null,},};},});
Usage in React
components/MyComponent.tsxtsximport {useEffect } from 'react';import {trpc } from '../utils/trpc';export functionMyComponent () {constmutation =trpc .addPost .useMutation ();useEffect (() => {mutation .mutate ({title : 'example' });}, []);if (mutation .error ?.data ?.zodError ) {// zodError will be inferredreturn (<pre >Error: {JSON .stringify (mutation .error .data .zodError , null, 2)}</pre >);}return <>[...]</>;}
components/MyComponent.tsxtsximport {useEffect } from 'react';import {trpc } from '../utils/trpc';export functionMyComponent () {constmutation =trpc .addPost .useMutation ();useEffect (() => {mutation .mutate ({title : 'example' });}, []);if (mutation .error ?.data ?.zodError ) {// zodError will be inferredreturn (<pre >Error: {JSON .stringify (mutation .error .data .zodError , null, 2)}</pre >);}return <>[...]</>;}
Per-procedure error formatting
Errors can be formatted on a per-procedure basis using the .errors() method, which can be chained to produce a union of possible error shapes for each procedure.
When a middleware or procedure throws an error, it bubbles upward through all .errors() handlers until one returns a shape or the global errorFormatter is reached.
server.tstsconstrateLimitedProcedure =t .procedure .errors ((opts ) => {if (opts .error .cause instanceofRateLimitError ) {return {...opts .shape ,data : { ...opts .shape .data ,kind : 'RATE_LIMIT' asconst },};}returnundefined ;}).use (opts => {// Important: errors bubble up so a throwing middleware must come after the .errors() handlerif (isRateLimited (opts )) {throw newRateLimitError ();}returnopts .next ();});constbilledProcedure =rateLimitedProcedure .errors ((opts ) => {if (opts .error .cause instanceofPaymentRequiredError ) {return {...opts .shape ,data : {...opts .shape .data ,kind : 'PAYMENT_REQUIRED' asconst ,amountDue :opts .error .cause .amountDue ,},};}returnundefined ;});constaddPostProcedure =billedProcedure .mutation (opts => {if (isPaymentRequired (opts )) {throw newPaymentRequiredError (100);}return {success : true,};});
server.tstsconstrateLimitedProcedure =t .procedure .errors ((opts ) => {if (opts .error .cause instanceofRateLimitError ) {return {...opts .shape ,data : { ...opts .shape .data ,kind : 'RATE_LIMIT' asconst },};}returnundefined ;}).use (opts => {// Important: errors bubble up so a throwing middleware must come after the .errors() handlerif (isRateLimited (opts )) {throw newRateLimitError ();}returnopts .next ();});constbilledProcedure =rateLimitedProcedure .errors ((opts ) => {if (opts .error .cause instanceofPaymentRequiredError ) {return {...opts .shape ,data : {...opts .shape .data ,kind : 'PAYMENT_REQUIRED' asconst ,amountDue :opts .error .cause .amountDue ,},};}returnundefined ;});constaddPostProcedure =billedProcedure .mutation (opts => {if (isPaymentRequired (opts )) {throw newPaymentRequiredError (100);}return {success : true,};});
All properties sent to errorFormatter()
tRPC is compliant with JSON-RPC 2.0
tsinterfaceErrorFormatterOpts {error :TRPCError ;type : 'query' | 'mutation' | 'subscription' | 'unknown';path : string | undefined;input : unknown;ctx : unknown;shape : {message : string;code : number;data : unknown };}
tsinterfaceErrorFormatterOpts {error :TRPCError ;type : 'query' | 'mutation' | 'subscription' | 'unknown';path : string | undefined;input : unknown;ctx : unknown;shape : {message : string;code : number;data : unknown };}
DefaultErrorShape:
tstypeDefaultErrorData = {code :TRPC_ERROR_CODE_KEY ;httpStatus : number;/*** Path to the procedure that threw the error*/path ?: string;/*** Stack trace of the error (only in development)*/stack ?: string;};interfaceDefaultErrorShape {message : string;code :TRPC_ERROR_CODE_NUMBER ;data :DefaultErrorData ;}
tstypeDefaultErrorData = {code :TRPC_ERROR_CODE_KEY ;httpStatus : number;/*** Path to the procedure that threw the error*/path ?: string;/*** Stack trace of the error (only in development)*/stack ?: string;};interfaceDefaultErrorShape {message : string;code :TRPC_ERROR_CODE_NUMBER ;data :DefaultErrorData ;}