# Errors

How problems in content and config are reported.

> Every page: https://tomekit.aayush.cv/llms.txt

tomekit reports every broken file in one pass, pointing at where to fix it.

## Content errors

A problem in a document is printed as `file:line:column: message`, with the file relative to the project root.

```text title="Terminal"
content/posts/hello.md:2:1: title: Invalid input: expected string, received undefined
content/posts/typo.md:2:1: author: no document in collection "authors" has the slug "adaa". Fix the slug, or add a document with it to "authors"
```

An entry from a loader without a file is named by its collection and slug instead: `pages.get("about"): message`.

- `vite build` fails, listing every broken file.
- The dev server logs the errors, shows them in the error overlay at the first one's location, and keeps serving everything else. Broken documents are left out until you fix them.

## Warnings

Warnings don't stop anything. Each one says what happens because of it:

```text title="Terminal"
posts: directory "content/posts" has no files, so the collection is empty
posts: no files in "content/posts" match "**/*.md", but it has 12 other files, so the collection is empty
```

## Config errors

A config that can't be used fails `vite build`. The dev server logs it and keeps running, but `tomekit/content` fails to load until you fix the config:

- `ConfigLoadError`: importing `tomekit.config.ts` threw, eg on a syntax error
- `MissingDefaultExportError`: the file doesn't `export default defineConfig(...)`
- `InvalidConfigError`: the config breaks a rule, eg a collection name that isn't a valid identifier, or a collection without a `loader`

Without the `tomekit()` plugin, importing `collections` throws `MissingPluginError`, while importing a collection by name fails first, with the module "does not provide an export named" it. Add the plugin to `vite.config.ts` in both cases, or outside Vite run Node with `--import tomekit/register` (see [Register](https://tomekit.aayush.cv/register/index.md)).

## Catching errors

Every error is a class exported from `tomekit`, so check it with `instanceof`:

| Class | Extends | Thrown when |
| --- | --- | --- |
| `TomekitError` | `Error` | The base of every error |
| `BrokenContentError` | `TomekitError` | A build has content errors. `errors` holds each `ContentError` |
| `ContentError` | `TomekitError` | One problem in a document. It has `file`, `line`, `column`, `collection` and `slug` |
| `ConfigError` | `TomekitError` | The base of config errors |
| `TransformError` | `TomekitError` | The base of errors from `transform`, set as a `ContentError`'s `cause` |
| `PluginError` | `TomekitError` | The base of errors from the Vite plugin |
| `MissingPluginError` | `PluginError` | `tomekit/content` is imported without the `tomekit()` plugin or `tomekit/register` |
| `UncacheablePluginError` | `PluginError` | `cached()` gets something other than one plugin with a `transform` hook |
| `DuplicateCachedPluginError` | `PluginError` | Two plugins wrapped in `cached()` have the same name (see [MDX](https://tomekit.aayush.cv/mdx/index.md#two-mdx-setups)) |

Vite wraps errors thrown by plugins and keeps the originals under `errors`:

```ts
import { BrokenContentError } from "tomekit";
import { build } from "vite";

const failure = await build().catch((cause: unknown) => cause);
const errors =
  failure instanceof Error && "errors" in failure ? failure.errors : [];

const broken = Array.isArray(errors)
  ? errors.find((error) => error instanceof BrokenContentError)
  : undefined;

broken?.errors[0]?.file; // "content/posts/hello.md"
```
