new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

tRPC · all subjects

error handling and formatting

25 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Custom error formatting example with Zod validation

Example showing how to add custom formatting to include Zod validation errors: create a tRPC instance with initTRPC.create() and define errorFormatter to check if error.code is 'BAD_REQUEST' and error.cause is a ZodError instance, then include error.cause.flatten() in the returned shape data.

Error formatting example code

```ts import { initTRPC } from '@trpc/server'; import { ZodError } from 'zod'; export const t = initTRPC.create({ errorFormatter(opts) { const { shape, error } = opts; return { ...shape, data: { ...shape.data, zodError: error.code === 'BAD_REQUEST' && error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); ```

Error formatting in routers is inferred to client

The error formatting defined in your tRPC router will be automatically inferred all the way to your client, enabling type-safe error handling.

Accessing custom error data in React client

When using tRPC React Query integration, custom error data added via errorFormatter is accessible and type-inferred on the client. For example, mutation.error?.data?.zodError will be inferred with proper typing when accessed in a React component using useMutation().

tRPC is JSON-RPC 2.0 compliant

tRPC follows the JSON-RPC 2.0 specification for error formatting.

DefaultErrorShape structure

The default error shape returned by tRPC contains: message (string), code (TRPC_ERROR_CODE_NUMBER), and data object. The data object contains code (TRPC_ERROR_CODE_KEY), httpStatus (number), path (optional string to the procedure that threw the error), and stack (optional string with stack trace, only in development).

errorFormatter option signature

The errorFormatter function receives an ErrorFormatterOpts object with properties: error (TRPCError), type ('query' | 'mutation' | 'subscription' | 'unknown'), path (string | undefined), input (unknown), ctx (unknown), and shape ({ message: string; code: number; data: unknown }).

onError handler example in standalone server

import { createHTTPServer } from '@trpc/server/adapters/standalone'; import { appRouter } from './router'; const server = createHTTPServer({ router: appRouter, onError(opts) { const { error, type, path, input, ctx, req } = opts; console.error('Error:', error); if (error.code === 'INTERNAL_SERVER_ERROR') { // send to bug reporting } }, });

OnErrorOpts interface definition

interface OnErrorOpts { error: TRPCError; type: 'query' | 'mutation' | 'subscription' | 'unknown'; path: string | undefined; input: unknown; ctx: unknown; req: Request; }

Error response example with code mapping

An example error response for bad request input looks like: {"id": null, "error": {"message": "\"password\" must be at least 4 characters", "code": -32600, "data": {"code": "BAD_REQUEST", "httpStatus": 400, "stack": "...", "path": "user.changepassword"}}}

TRPCError throwing example

import { initTRPC, TRPCError } from '@trpc/server'; const t = initTRPC.create(); const theError = new Error('something went wrong'); const appRouter = t.router({ hello: t.procedure.query(() => { throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'An unexpected error occurred, please try again later.', // optional: pass the original error to retain stack trace cause: theError, }); }), });

Error response object structure

When an error occurs in a procedure, tRPC responds to the client with an object containing an 'error' property. The error object includes a message, a code (as a negative integer), and a data object. The data object contains the code name (like 'BAD_REQUEST'), the HTTP status, stack trace (if isDev is true), and the path where the error occurred.

Stack traces in production mode

By default, tRPC includes error.data.stack only when isDev is true. initTRPC.create() sets isDev to process.env.NODE_ENV !== 'production' by default. To override this behavior and control stack trace visibility, manually set isDev when creating tRPC with initTRPC.create({ isDev: false }).

tRPC error codes and HTTP status mapping

tRPC defines these error codes with their descriptions and HTTP status codes: PARSE_ERROR (400) - Invalid JSON or parsing error; BAD_REQUEST (400) - Client error in request; UNAUTHORIZED (401) - Missing authentication credentials; PAYMENT_REQUIRED (402) - Payment required for resource; FORBIDDEN (403) - Client not authorized to access resource; NOT_FOUND (404) - Server cannot find resource; METHOD_NOT_SUPPORTED (405) - Request method not supported for resource; TIMEOUT (408) - Server wants to shut down unused connection; CONFLICT (409) - Request conflicts with current resource state; PRECONDITION_FAILED (412) - Access to resource denied; PAYLOAD_TOO_LARGE (413) - Request entity larger than server limits; UNSUPPORTED_MEDIA_TYPE (415) - Payload format unsupported; UNPROCESSABLE_CONTENT (422) - Server unable to process correct request; PRECONDITION_REQUIRED (428) - Required precondition header missing; TOO_MANY_REQUESTS (429) - Rate limit exceeded or too many requests; CLIENT_CLOSED_REQUEST (499) - Client closed connection before server finished responding; INTERNAL_SERVER_ERROR (500) - Unspecified error; NOT_IMPLEMENTED (501) - Server does not support required functionality; BAD_GATEWAY (502) - Invalid response from upstream server; SERVICE_UNAVAILABLE (503) - Server not ready to handle request; GATEWAY_TIMEOUT (504) - No response from upstream server in time.

getHTTPStatusCodeFromError helper function

tRPC provides getHTTPStatusCodeFromError imported from '@trpc/server/http' to extract the HTTP status code from a TRPCError. The function takes a TRPCError object and returns its corresponding HTTP status code as an integer.

TRPCError class for throwing errors

tRPC provides a TRPCError subclass that you can throw from procedures to represent errors. When creating a TRPCError, pass an object with: code (required) - one of the defined error codes; message (required) - error message to send to client; cause (optional) - the original error to retain the stack trace.

onError handler in server configuration

All errors that occur in a procedure go through the onError method before being sent to the client. The onError method receives an object with properties: error (TRPCError), type ('query' | 'mutation' | 'subscription' | 'unknown'), path (string | undefined), input (unknown), ctx (unknown), and req (Request). This is where you can handle errors like sending them to bug reporting services.

Throwing TRPCError in middleware for access control

When building middleware or base procedures for access control, use throw new TRPCError() with appropriate error codes such as 'UNAUTHORIZED' for unauthenticated users or 'FORBIDDEN' for users without required permissions.

createCaller onError option

Both createCallerFactory and createCaller can take an onError option to handle errors. The handler receives the same arguments as an error formatter except for the shape field. Any handler passed to createCallerFactory will be called before the handler passed to createCaller.

OnErrorShape interface for createCaller

The error handler passed to createCaller or createCallerFactory receives an object with this shape: ```ts interface OnErrorShape { ctx: unknown; error: TRPCError; path: string | undefined; input: unknown; type: 'query' | 'mutation' | 'subscription' | 'unknown'; } ```

createCaller onError handler example

Example showing onError handler with createCaller: ```ts const t = initTRPC.context<{ foo?: 'bar' }>().create(); const router = t.router({ greeting: t.procedure.input(z.object({ name: z.string() })).query((opts) => { if (opts.input.name === 'invalid') { throw new Error('Invalid name'); } return `Hello ${opts.input.name}`; }), }); const caller = router.createCaller( {}, { onError: (opts) => { console.error('An error occurred:', opts.error); }, }, ); await caller.greeting({ name: 'invalid' }); ```

Error handling in subscriptions

Throwing an error in a generator function propagates to tRPC's onError() on the backend. If the error thrown is a 5xx error, the client will automatically attempt to reconnect based on the last event id that is tracked using tracked(). For other errors, the subscription will be cancelled and propagate to the onError() callback.

TRPCError for throwing NOT_FOUND

Import TRPCError from '@trpc/server' and throw new TRPCError({ code: 'NOT_FOUND' }) when a resource cannot be found.

tRPC error handling and formatting

Throw typed errors from procedures, format errors for clients, and implement global error handling. Refer to error-handling skill for patterns.

Output validation failure response

If output validation fails, the server will respond with an INTERNAL_SERVER_ERROR.

Give your agent this brain