---
title: Error Handling
description: Catch FetchError and ValidationError, and handle parsing, abort, and network failures from fetch calls.
type: package
package: "@zap-studio/fetch"
---

`fetch` throws `FetchError` for HTTP failures and `ValidationError` for schema failures. Platform and schema errors can still propagate from native `fetch`, body parsing, request construction, or the validator itself.

| Error             | Import path  | Thrown when                                                                                        |
| ----------------- | ------------ | -------------------------------------------------------------------------------------------------- |
| `FetchError`      | `fetch`      | The response is not ok and `throwOnFetchError` is `true` (default).                                |
| `ValidationError` | `validation` | A schema is provided, validation returns issues, and `throwOnValidationError` is `true` (default). |

```ts
import { FetchError } from "@zap-studio/fetch";
import { ValidationError } from "@zap-studio/validation";
```

## FetchError

Thrown for non-2xx responses before the body is parsed or validated. Its message has the form `HTTP <status>: <statusText>`.

| Property   | Type       | Description                                                       |
| ---------- | ---------- | ----------------------------------------------------------------- |
| `status`   | `number`   | HTTP status code from the failing response.                       |
| `response` | `Response` | Full response object for further inspection (headers, body, ...). |

```ts
import { api, FetchError } from "@zap-studio/fetch";
import { z } from "zod";

const UserSchema = z.object({ id: z.number(), name: z.string() });

try {
  await api.get("https://api.example.com/users/missing", UserSchema);
} catch (error) {
  if (error instanceof FetchError) {
    console.error(error.status);
    console.error(await error.response.text());
  } else {
    throw error;
  }
}
```

Set `throwOnFetchError: false` to handle the response manually.

```ts
import { $fetch } from "@zap-studio/fetch";

const response = await $fetch("https://api.example.com/users/missing", {
  throwOnFetchError: false,
});

if (!response.ok) {
  console.error(response.status);
}
```

:::note

With `throwOnFetchError: false` and a schema provided, the error response body is still parsed and validated — which usually produces a `ValidationError` or `SyntaxError` for non-JSON error pages. Prefer schema-less `$fetch` when you need to inspect error responses.

:::

## ValidationError

Thrown when the response body does not match the provided schema. Its message is a JSON string of the issues.

| Property | Type                                | Description                               |
| -------- | ----------------------------------- | ----------------------------------------- |
| `issues` | `readonly StandardSchemaV1.Issue[]` | Validation issues reported by the schema. |

```ts
import { api } from "@zap-studio/fetch";
import { ValidationError } from "@zap-studio/validation";
import { z } from "zod";

const UserSchema = z.object({ id: z.number(), name: z.string() });

try {
  await api.get("https://api.example.com/users/1", UserSchema);
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(error.issues);
  } else {
    throw error;
  }
}
```

Set `throwOnValidationError: false` to receive the Standard Schema result object instead.

```ts
import { api } from "@zap-studio/fetch";
import { z } from "zod";

const UserSchema = z.object({ id: z.number(), name: z.string() });

const result = await api.get("https://api.example.com/users/1", UserSchema, {
  throwOnValidationError: false,
});

if (result.issues) {
  console.error(result.issues);
}
```

See [Validation](/fetch/validation) for the full validation flow.

## Other Throwable Errors

Calls can also reject with:

- `TypeError` — both `body` and `json` provided, invalid headers or search params, request construction failure, JSON request serialization failure, network failure, or a response body read failure.
- `SyntaxError` — a schema is provided and `response.json()` cannot parse the body.
- `DOMException` — the request or response body read is aborted (`AbortError`).
- Any error thrown or rejected by the provided Standard Schema validator.

## Exhaustive Handling

Catch package-specific errors first, then rethrow unknown failures.

```ts
import { api, FetchError } from "@zap-studio/fetch";
import { ValidationError } from "@zap-studio/validation";
import { z } from "zod";

const UserSchema = z.object({ id: z.number(), name: z.string() });

async function loadUser(id: string) {
  try {
    const user = await api.get(`https://api.example.com/users/${id}`, UserSchema);
    return { ok: true as const, user };
  } catch (error) {
    if (error instanceof FetchError) {
      return { ok: false as const, reason: "http", status: error.status };
    }

    if (error instanceof ValidationError) {
      return { ok: false as const, reason: "validation", issues: error.issues };
    }

    throw error;
  }
}
```
