---
title: Error Handling
sidebar:
  icon: circle-alert
description: Throw a typed error from a procedure. The client sees the message, not the stack.
---

Throw `RpcError` from a procedure or middleware for a custom JSON-RPC error. Plain `Error` becomes `INTERNAL_ERROR` with a generic message (`"Internal error"`). No stack or original message is leaked.

> **Security:** `RpcError.data` is serialized to the wire. Never put secrets, stack traces, or internal state in it.

```ts twoslash
import { APP_ERROR_RANGE, JSON_RPC_ERROR, RpcError } from "tacho";

throw new RpcError({
  code: -32001,
  message: "nope",
  data: { retry: true },
});

JSON_RPC_ERROR.PARSE_ERROR;
JSON_RPC_ERROR.INVALID_REQUEST;
JSON_RPC_ERROR.METHOD_NOT_FOUND;
JSON_RPC_ERROR.INVALID_PARAMS;
JSON_RPC_ERROR.INTERNAL_ERROR;
APP_ERROR_RANGE;
```

`rpcResult` throws `RpcError` on a JSON-RPC error object. Clients check with `instanceof RpcError`.
