# Error Handling

> Send errors by throwing an HTTPError.

H3 captures all possible errors during [request lifecycle](/guide/basics/lifecycle).

## `HTTPError`

You can create and throw HTTP errors using `HTTPError` with different syntaxes.

```js
import { HTTPError } from "h3";

app.get("/error", (event) => {
  // Using message and details
  throw new HTTPError("Invalid user input", { status: 400 });

  // Using HTTPError.status(code)
  throw HTTPError.status(400, "Bad Request");

  // Using single pbject
  throw new HTTPError({
    status: 400,
    statusText: "Bad Request",
    message: "Invalid user input",
    data: { field: "email" },
    body: { date: new Date().toJSON() },
    headers: {},
  });
});
```

This will end the request with `400 - Bad Request` status code and the following JSON response:

```json
{
  "date": "2025-06-05T04:20:00.0Z",
  "status": 400,
  "statusText": "Bad Request",
  "message": "Invalid user input",
  "data": {
    "field": "email"
  }
}
```

### `HTTPError` Fields

- `status`: HTTP status code in the range 200–599.
- `statusText`: HTTP status text to be sent in the response header.
- `message`: Error message to be included in the JSON body.
- `data`: Additional data to be attached under the `data` key in the error JSON body.
- `body`: Additional top-level properties to be attached in the error JSON body.
- `headers`: Additional HTTP headers to be sent in the error response.
- `cause`: The original error object that caused this error, useful for tracing and debugging.
- `unhandled`: Indicates whether the error was thrown for unknown reasons. See [Unhandled Errors](#unhandled-errors).

> [!TIP]
The recommended way to include headers in error responses is to use `new HTTPError({ headers })`:

> ```js
> throw new HTTPError({
>   status: 400,
>   message: "Invalid input",
>   headers: { "x-request-id": requestId },
> });
> ```

> When an error is thrown, any [prepared headers](/guide/basics/response#preparing-response) set via `event.res.headers` are **not** included in the error response. As a last resort for headers that need to be set implicitly before the error is known (e.g., CORS headers), you can use `event.res.errHeaders`. Built-in utilities like `handleCors` automatically set both.

> [!IMPORTANT]
> Error `statusText` should be short (max 512 to 1024 characters) and only include tab, spaces or visible ASCII characters and extended characters (byte value 128–255). Prefer `message` in JSON body for extended message.

## Unhandled Errors

Any error that occurs during calling [request lifecycle](/guide/basics/lifecycle) without using `HTTPError` will be processed as an <u>unhandled</u> error.

```js
app.get("/error", (event) => {
  // This will cause an unhandled error.
  throw new Error("Something went wrong");
});
```

> [!TIP]
> For enhanced security, H3 hides certain fields of unhandled errors (`data`, `body`, `stack` and `message`) in JSON response.

## Catching Errors

Using global [`onError`](/guide/api/h3#global-hooks) hook:

```js
import { H3, onError } from "h3";

// Globally handling errors
const app = new H3({
  onError: (error) => {
    console.error(error);
  },
});
```

Using [`onError` middleware](/guide/basics/middleware) to catch errors.

```js
import { onError } from "h3";

// Handling errors using middleware
app.use(
  onError((error, event) => {
    console.error(error);
  }),
);
```

> [!TIP]
> When using nested apps, global hooks of sub-apps will not be called. Therefore it is better to use `onError` middleware.
