Skip to content
tRPC

Errors

One error class, typed on the client from what resolvers return, with or without declarations.

On this page

There is one error class, TrpcError. Return error({ code }) from a resolver or a middleware, or fail with it in Effect code (Effect.fail(error(…)), yield* error(…)). The procedure's error union is inferred from those, with no declaration:

import { function error<const TCode extends string, TData = undefined>(opts: Omit<TrpcErrorOptions<TCode, TData>, "defined">): TrpcError<TCode, TData>error } from '@trpc12/trpc/server';
import { import zz } from 'zod';

const 
const byId: BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>, "query", {
    output: Post;
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }>;
    services: never;
    unmapped: never;
}>
byId
= const t: TrpcRoot<Context, {}, RootDef<Context, {}>>t.TrpcRoot<Context, {}, RootDef<Context, {}>>.procedure: ProcedureBuilder<RootDef<Context, {}>>procedure
.
ProcedureBuilder<RootDef<Context, {}>>.input<z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<InputDef<RootDef<Context, {}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>> (+1 overload)
input
(import zz.
function object<{
    id: z.ZodString;
}>(shape?: {
    id: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>
object
({ id: z.ZodStringid: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }))
.
ProcedureBuilder<InputDef<RootDef<Context, {}>, ZodObject<{ id: ZodString; }, $strip>>>.query<Post | TrpcError<"NOT_FOUND", {
    id: string;
}>>(resolver: (opts: ResolverOpts<InputDef<RootDef<Context, {}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>) => Post | TrpcError<"NOT_FOUND", {
    id: string;
}>): BuildProcedure<InputDef<RootDef<Context, {}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>, "query", {
    output: Post;
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }>;
    services: never;
    unmapped: never;
}>
query
(
(
opts: ResolverOpts<InputDef<RootDef<Context, {}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>
opts
) =>
const db: {
    post: {
        find: (id: string) => Post | undefined;
        list: () => Post[];
        create: (input: {
            title: string;
            author?: string | null;
        }) => Post;
    };
}
db
.
post: {
    find: (id: string) => Post | undefined;
    list: () => Post[];
    create: (input: {
        title: string;
        author?: string | null;
    }) => Post;
}
post
.find: (id: string) => Post | undefinedfind(
opts: ResolverOpts<InputDef<RootDef<Context, {}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>
opts
.
ResolverOpts<InputDef<RootDef<Context, {}>, ZodObject<{ id: ZodString; }, $strip>>>.input: {
    id: string;
}
input
.id: stringid) ??
error<"NOT_FOUND", {
    id: string;
}>(opts: Omit<TrpcErrorOptions<"NOT_FOUND", {
    id: string;
}>, "defined">): TrpcError<"NOT_FOUND", {
    id: string;
}>
error
({ code: "NOT_FOUND"code: 'NOT_FOUND',
data?: {
    id: string;
} | undefined
data
: { id: stringid:
opts: ResolverOpts<InputDef<RootDef<Context, {}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>
opts
.
ResolverOpts<InputDef<RootDef<Context, {}>, ZodObject<{ id: ZodString; }, $strip>>>.input: {
    id: string;
}
input
.id: stringid } }),
);

Declaring errors

Declaring errors with .errors() is optional. It adds typed constructors, data schemas and entries in generateOpenApi's document. It also types what inference can't see: errors thrown from helpers (a throw has no type) and contracts, which have no resolver. experimental_generateOpenApiFromTypes documents inferred errors without declarations (The document).

import { import zz } from 'zod';

const 
const byId: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
        GONE: TrpcError<"GONE", undefined>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }> | TrpcError<"GONE", undefined>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>, "query", {
    output: Post;
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }>;
    services: never;
    unmapped: never;
}>
byId
= const t: TrpcRoot<Context, {}, RootDef<Context, {}>>t.TrpcRoot<Context, {}, RootDef<Context, {}>>.procedure: ProcedureBuilder<RootDef<Context, {}>>procedure
.
ProcedureBuilder<RootDef<Context, {}>>.errors<{
    readonly NOT_FOUND: {
        readonly data: z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>;
    };
    readonly GONE: {
        readonly status: 410;
        readonly message: "This post was removed";
    };
}>(map: {
    readonly NOT_FOUND: {
        readonly data: z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>;
    };
    readonly GONE: {
        readonly status: 410;
        readonly message: "This post was removed";
    };
}): ProcedureBuilder<With<RootDef<Context, {}>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
        GONE: TrpcError<"GONE", undefined>;
    };
    errors: TrpcError<...> | TrpcError<...>;
}>>
errors
({
type NOT_FOUND: {
    readonly data: z.ZodObject<{
        id: z.ZodString;
    }, z.core.$strip>;
}
NOT_FOUND
: {
data: z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>
data
: import zz.
function object<{
    id: z.ZodString;
}>(shape?: {
    id: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>
object
({ id: z.ZodStringid: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }) },
type GONE: {
    readonly status: 410;
    readonly message: "This post was removed";
}
GONE
: { status: 410status: 410, message: "This post was removed"message: 'This post was removed' },
}) .
ProcedureBuilder<With<RootDef<Context, {}>, { declared: { NOT_FOUND: TrpcError<"NOT_FOUND", { id: string; }>; GONE: TrpcError<"GONE", undefined>; }; errors: TrpcError<...> | TrpcError<...>; }>>.input<z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<InputDef<With<RootDef<Context, {}>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
        GONE: TrpcError<"GONE", undefined>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }> | TrpcError<"GONE", undefined>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>> (+1 overload)
input
(import zz.
function object<{
    id: z.ZodString;
}>(shape?: {
    id: z.ZodString;
} | undefined, params?: string | {
    error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
    message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>
object
({ id: z.ZodStringid: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }))
.
ProcedureBuilder<InputDef<With<RootDef<Context, {}>, { declared: { NOT_FOUND: TrpcError<"NOT_FOUND", { id: string; }>; GONE: TrpcError<"GONE", undefined>; }; errors: TrpcError<...> | TrpcError<...>; }>, ZodObject<...>>>.query<Post | TrpcError<"NOT_FOUND", {
    id: string;
}>>(resolver: (opts: ResolverOpts<InputDef<With<RootDef<Context, {}>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
        GONE: TrpcError<"GONE", undefined>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }> | TrpcError<"GONE", undefined>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>) => Post | TrpcError<"NOT_FOUND", {
    id: string;
}>): BuildProcedure<...>
query
(
(
opts: ResolverOpts<InputDef<With<RootDef<Context, {}>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
        GONE: TrpcError<"GONE", undefined>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }> | TrpcError<"GONE", undefined>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>
opts
) =>
const db: {
    post: {
        find: (id: string) => Post | undefined;
        list: () => Post[];
        create: (input: {
            title: string;
            author?: string | null;
        }) => Post;
    };
}
db
.
post: {
    find: (id: string) => Post | undefined;
    list: () => Post[];
    create: (input: {
        title: string;
        author?: string | null;
    }) => Post;
}
post
.find: (id: string) => Post | undefinedfind(
opts: ResolverOpts<InputDef<With<RootDef<Context, {}>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
        GONE: TrpcError<"GONE", undefined>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }> | TrpcError<"GONE", undefined>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>
opts
.
ResolverOpts<InputDef<With<RootDef<Context, {}>, { declared: { NOT_FOUND: TrpcError<"NOT_FOUND", { id: string; }>; GONE: TrpcError<"GONE", undefined>; }; errors: TrpcError<...> | TrpcError<...>; }>, ZodObject<...>>>.input: {
    id: string;
}
input
.id: stringid) ??
opts: ResolverOpts<InputDef<With<RootDef<Context, {}>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
        GONE: TrpcError<"GONE", undefined>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }> | TrpcError<"GONE", undefined>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>
opts
.
ResolverOpts<InputDef<With<RootDef<Context, {}>, { declared: { NOT_FOUND: TrpcError<"NOT_FOUND", { id: string; }>; GONE: TrpcError<"GONE", undefined>; }; errors: TrpcError<...> | TrpcError<...>; }>, ZodObject<...>>>.errors: ErrorConstructors<{
    NOT_FOUND: TrpcError<"NOT_FOUND", {
        id: string;
    }>;
    GONE: TrpcError<"GONE", undefined>;
}>
errors
.
type NOT_FOUND: (opts: ErrorConstructorOpts<{
    id: string;
}>) => TrpcError<"NOT_FOUND", {
    id: string;
}>
NOT_FOUND
({
data: {
    id: string;
}
data
: { id: stringid:
opts: ResolverOpts<InputDef<With<RootDef<Context, {}>, {
    declared: {
        NOT_FOUND: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
        GONE: TrpcError<"GONE", undefined>;
    };
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }> | TrpcError<"GONE", undefined>;
}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>
opts
.
ResolverOpts<InputDef<With<RootDef<Context, {}>, { declared: { NOT_FOUND: TrpcError<"NOT_FOUND", { id: string; }>; GONE: TrpcError<"GONE", undefined>; }; errors: TrpcError<...> | TrpcError<...>; }>, ZodObject<...>>>.input: {
    id: string;
}
input
.id: stringid } }),
);

On the client

On the client, error.defined is true for errors in the union, whether returned, failed or declared (narrow on code next). It is false for anything else: undeclared thrown errors, network failures, aborts. Unexpected errors' messages are masked unless NODE_ENV is development or test, or exposeUnexpectedErrors: true. There is no errorFormatter.

import { 
function safe<T, E = unknown>(promise: PromiseLike<T> & {
    readonly "~error"?: E;
}): Promise<SafeResult<T, E>>
safe
} from '@trpc12/trpc/client';
const [
const post: {
    id: string;
    title: string;
    author: string | null;
    createdAt: string;
} | undefined
post
,
const err: UnexpectedError | (TrpcError<"NOT_FOUND", {
    id: string;
}> & {
    readonly defined: true;
}) | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
    readonly code: "invalid_type";
    readonly expected: $ZodInvalidTypeExpected;
    readonly path: readonly [] | readonly ["id"];
    readonly message: string;
} | {
    readonly code: "too_big";
    readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    readonly maximum: string | number;
    readonly inclusive?: boolean | undefined;
    readonly exact?: boolean | undefined;
    readonly path: readonly [] | readonly [...];
    readonly message: string;
} | ... 9 more ... | {
    ...;
}>> & {
    ...;
}) | undefined
err
] = await
safe<{
    id: string;
    title: string;
    author: string | null;
    createdAt: string;
}, UnexpectedError | (TrpcError<"NOT_FOUND", {
    id: string;
}> & {
    readonly defined: true;
}) | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
    readonly code: "invalid_type";
    readonly expected: $ZodInvalidTypeExpected;
    readonly path: readonly [] | readonly ["id"];
    readonly message: string;
} | {
    readonly code: "too_big";
    readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    ... 4 more ...;
    readonly message: string;
} | ... 9 more ... | {
    ...;
}>> & {
    ...;
})>(promise: PromiseLike<...> & {
    ...;
}): Promise<...>
safe
(
const client: TrpcClient<{
    post: {
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
            id: ZodString;
        }, $strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<RootDef<Context, {}>, "query", {
            output: Post[];
            errors: never;
            services: never;
            unmapped: never;
        }>;
        create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
            ctx: Context;
            ctxAdded: {
                ...;
            };
            inputOut: Unset;
            errors: TrpcError<...>;
            shorts: never;
        }>, ZodObject<...>>, "mutation", {
            ...;
        }>;
    };
}, {}, undefined, LinksTypes<...>>
client
.
post: ClientRouter<{
    byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
        id: ZodString;
    }, $strip>>, "query", {
        output: Post;
        errors: TrpcError<"NOT_FOUND", {
            id: string;
        }>;
        services: never;
        unmapped: never;
    }>;
    list: BuildProcedure<RootDef<Context, {}>, "query", {
        output: Post[];
        errors: never;
        services: never;
        unmapped: never;
    }>;
    create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
        ctx: Context;
        ctxAdded: {
            ...;
        };
        inputOut: Unset;
        errors: TrpcError<...>;
        shorts: never;
    }>, ZodObject<...>>, "mutation", {
        ...;
    }>;
}, {}, undefined, LinksTypes<...>>
post
.
byId: {
    query(input: {
        id: string;
    }, opts?: CallOptions<{}> | undefined): TrpcPromise<{
        id: string;
        title: string;
        author: string | null;
        createdAt: string;
    }, CallError<{
        type: "query";
        ctx: Context;
        meta: {};
        input: {
            id: string;
        };
        output: Post;
        errors: TrpcError<"NOT_FOUND", {
            id: string;
        }> | InputValidationError<{
            readonly code: "invalid_type";
            readonly expected: $ZodInvalidTypeExpected;
            readonly path: readonly [] | readonly ["id"];
            readonly message: string;
        } | {
            readonly code: "too_big";
            readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
            ... 4 more ...;
            readonly message: string;
        } | ... 9 more ... | {
            ...;
        }>;
        services: never;
    }, JsonForm, never>>;
}
byId
.
function query(input: {
    id: string;
}, opts?: CallOptions<{}> | undefined): TrpcPromise<{
    id: string;
    title: string;
    author: string | null;
    createdAt: string;
}, CallError<{
    type: "query";
    ctx: Context;
    meta: {};
    input: {
        id: string;
    };
    output: Post;
    errors: TrpcError<"NOT_FOUND", {
        id: string;
    }> | InputValidationError<{
        readonly code: "invalid_type";
        readonly expected: $ZodInvalidTypeExpected;
        readonly path: readonly [] | readonly ["id"];
        readonly message: string;
    } | {
        readonly code: "too_big";
        readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
        ... 4 more ...;
        readonly message: string;
    } | ... 9 more ... | {
        ...;
    }>;
    services: never;
}, JsonForm, never>>
query
({ id: stringid: '1' }));
if (
const err: UnexpectedError | (TrpcError<"NOT_FOUND", {
    id: string;
}> & {
    readonly defined: true;
}) | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
    readonly code: "invalid_type";
    readonly expected: $ZodInvalidTypeExpected;
    readonly path: readonly [] | readonly ["id"];
    readonly message: string;
} | {
    readonly code: "too_big";
    readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    readonly maximum: string | number;
    readonly inclusive?: boolean | undefined;
    readonly exact?: boolean | undefined;
    readonly path: readonly [] | readonly [...];
    readonly message: string;
} | ... 9 more ... | {
    ...;
}>> & {
    ...;
}) | undefined
err
?.defined: boolean | undefineddefined &&
const err: (TrpcError<"NOT_FOUND", {
    id: string;
}> & {
    readonly defined: true;
}) | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
    readonly code: "invalid_type";
    readonly expected: $ZodInvalidTypeExpected;
    readonly path: readonly [] | readonly ["id"];
    readonly message: string;
} | {
    readonly code: "too_big";
    readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    readonly maximum: string | number;
    readonly inclusive?: boolean | undefined;
    readonly exact?: boolean | undefined;
    readonly path: readonly [] | readonly [...];
    readonly message: string;
} | ... 9 more ... | {
    ...;
}>> & {
    ...;
})
err
.TrpcError<TCode extends string = string, TData = unknown>.code: "NOT_FOUND" | "BAD_REQUEST"code === 'NOT_FOUND') {
var console: Consoleconsole.Console.log(...data: any[]): voidlog(
const err: TrpcError<"NOT_FOUND", {
    id: string;
}> & {
    readonly defined: true;
}
err
.data);
TrpcError<"NOT_FOUND", { id: string; }>.data: {
    id: string;
}
}

See Errors without try/catch for safe() and createSafeClient().

Codes and statuses

Any string is a code. The built-in ones (NOT_FOUND, UNAUTHORIZED, …) have their HTTP status; a custom code is a 400 unless its declaration (.errors() or a TrpcError.Tagged class) has a status. The status is for HTTP: errors don't carry it, and neither do error bodies or socket frames.

const t: TrpcRoot<Context, {}, RootDef<Context, {}>>t.TrpcRoot<Context, {}, RootDef<Context, {}>>.procedure: ProcedureBuilder<RootDef<Context, {}>>procedure
  .
ProcedureBuilder<RootDef<Context, {}>>.errors<{
    readonly OUT_OF_STOCK: {
        readonly status: 409;
    };
}>(map: {
    readonly OUT_OF_STOCK: {
        readonly status: 409;
    };
}): ProcedureBuilder<With<RootDef<Context, {}>, {
    declared: {
        OUT_OF_STOCK: TrpcError<"OUT_OF_STOCK", undefined>;
    };
    errors: TrpcError<"OUT_OF_STOCK", undefined>;
}>>
errors
({
type OUT_OF_STOCK: {
    readonly status: 409;
}
OUT_OF_STOCK
: { status: 409status: 409 } })
.
ProcedureBuilder<With<RootDef<Context, {}>, { declared: { OUT_OF_STOCK: TrpcError<"OUT_OF_STOCK", undefined>; }; errors: TrpcError<"OUT_OF_STOCK", undefined>; }>>.mutation<TrpcError<"OUT_OF_STOCK", undefined>>(resolver: (opts: ResolverOpts<With<RootDef<Context, {}>, {
    declared: {
        OUT_OF_STOCK: TrpcError<"OUT_OF_STOCK", undefined>;
    };
    errors: TrpcError<"OUT_OF_STOCK", undefined>;
}>>) => TrpcError<"OUT_OF_STOCK", undefined>): BuildProcedure<With<RootDef<Context, {}>, {
    declared: {
        OUT_OF_STOCK: TrpcError<"OUT_OF_STOCK", undefined>;
    };
    errors: TrpcError<"OUT_OF_STOCK", undefined>;
}>, "mutation", {
    ...;
}>
mutation
(() => error<"OUT_OF_STOCK", undefined>(opts: Omit<TrpcErrorOptions<"OUT_OF_STOCK", undefined>, "defined">): TrpcError<"OUT_OF_STOCK", undefined>error({ code: "OUT_OF_STOCK"code: 'OUT_OF_STOCK' })); // 409
const t: TrpcRoot<Context, {}, RootDef<Context, {}>>t.TrpcRoot<Context, {}, RootDef<Context, {}>>.procedure: ProcedureBuilder<RootDef<Context, {}>>procedure.
ProcedureBuilder<RootDef<Context, {}>>.mutation<TrpcError<"OUT_OF_STOCK", undefined>>(resolver: (opts: ResolverOpts<RootDef<Context, {}>>) => TrpcError<"OUT_OF_STOCK", undefined>): BuildProcedure<RootDef<Context, {}>, "mutation", {
    output: never;
    errors: TrpcError<"OUT_OF_STOCK", undefined>;
    services: never;
    unmapped: never;
}>
mutation
(() => error<"OUT_OF_STOCK", undefined>(opts: Omit<TrpcErrorOptions<"OUT_OF_STOCK", undefined>, "defined">): TrpcError<"OUT_OF_STOCK", undefined>error({ code: "OUT_OF_STOCK"code: 'OUT_OF_STOCK' })); // 400

In Effect code

An error's _tag is its code, so Effect code narrows with catchTag:

Effect.catchTag('NOT_FOUND', () => Effect.succeed(null));

A failure that isn't a TrpcError is a type error until it is mapped, so internals never reach clients. TrpcError.Tagged classes mark the errors clients may see (Effect).