---
title: Errors
description: The full tagged-error catalog, covering engine errors, wire-only tags, and how each failure surfaces to a caller.
---

Every fallible call in Lakefront fails into one of two catalogs: engine errors, raised inside the engine and carried unchanged across the wire, and wire-only tags, synthesised by the transport or the server before a handler runs. This page lists both, with the fields and conditions behind each.

## How failures surface

`Engine` methods and `BoardClient`'s mutation methods return `Result<T, WireError>` (from `better-result`): check `isErr()`, branch on `error._tag`, or call `.unwrap()` for the happy path.

```ts
const outcome = await board.insertMany(rows);
if (outcome.isErr()) {
  console.error(outcome.error._tag, outcome.error.message);
} else {
  console.log("committed at", outcome.value);
}
```

Inside `engine.transaction(...)`, `TransactionOps` methods **throw** instead: the transaction absorbs the failure and rolls back, so there's no `Result` for call sites inside the callback to forget to check.

The client's builder reads (`select`, `selectAll`, `db`) also throw: Kysely's `Driver` interface is exception-based, so `LakefrontConnection.executeQuery` is the one place a `Result` is deliberately unwrapped.

Underneath, transports raise `RpcError` (from `lakefront/contract`) for a non-200 response with no JSON envelope. `createRpcClient`'s retry loop always catches it internally and converts the final failure to a `TransportFailed` `Result` rather than letting it escape as a throw. See the [client reference](/reference/client).

## Engine errors

Raised by the engine; the same `_tag` crosses the wire unchanged as a `WireError`.

| Tag                  | Fields                                                 | When                                                                                                                                                                                                                                                                                                                  |
| -------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `InvalidBoardId`     | `boardId`, `message`                                   | A board id fails `/^[A-Za-z0-9_]{1,64}$/u`: board ids are interpolated into table names, so they're validated rather than escaped.                                                                                                                                                                                    |
| `EntityNotAllowed`   | `entity`, `message`                                    | A placeholder bound to an entity outside the server's allowlist: the board-isolation boundary refusing.                                                                                                                                                                                                               |
| `PlaceholderUnbound` | `token`, `message`                                     | A `{{token}}` placeholder either has an invalid name or reached execution still unbound.                                                                                                                                                                                                                              |
| `LakeUnavailable`    | `message`, `cause?`                                    | The lake couldn't be attached: catalog unreachable, or the concurrent first-boot race exhausted its retries.                                                                                                                                                                                                          |
| `QueryFailed`        | `sql`, `message`, `cause?`                             | A query failed inside DuckDB. Carries the statement so the failure is diagnosable without re-running it.                                                                                                                                                                                                              |
| `SyncFailed`         | `boardId`, `from`, `to`, `message`, `cause?`           | Applying the change feed to a replica failed. The replica is a cache, so recovery is always rebuild, never repair.                                                                                                                                                                                                    |
| `ReplicaUnavailable` | `boardId`, `message`, `cause?`                         | A replica could not be materialised from the lake.                                                                                                                                                                                                                                                                    |
| `WriteFailed`        | `boardId`, `message`, `cause?`                         | A write did not commit. Distinct from a read failure: it may have landed, so it must not be retried blindly.                                                                                                                                                                                                          |
| `ReservedColumnName` | `boardId`, `column`, `message`                         | A write named a column with the engine's reserved `_` prefix: those carry the version stamps that upsert ordering, compaction, and replica deletes all read.                                                                                                                                                          |
| `ColumnTypeConflict` | `boardId`, `column`, `columnType`, `message`, `cause?` | A value can't be stored in the column's current type and the engine won't reshape the board silently. For example, a string written into a column inferred as `double`. Fix with an explicit `widenColumn(board, column, to)`, naming the lowercase `ColumnType` vocabulary (see the [schema guide](/guides/schema)). |
| `BoardNotFound`      | `boardId`, `message`                                   | The board doesn't exist: reading one never written to, writing under `autoCreate: "never"`, or touching one another node has since dropped.                                                                                                                                                                           |
| `BoardExists`        | `boardId`, `message`                                   | `createBoard` on a board that's already there.                                                                                                                                                                                                                                                                        |
| `UnknownColumn`      | `boardId`, `column`, `message`                         | A write named a column that doesn't exist, under engine-wide `columns: "declared"` or a board created with `strict: true`. See `createBoard` in the [engine reference](/reference/engine).                                                                                                                            |

## Wire-only tags

Never raised by the engine: synthesised at the client transport or the server's request handling, before (or instead of) a handler running.

| Tag                    | HTTP status     | When                                                                                                                                                          |
| ---------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Unauthenticated`      | `401`           | Missing or invalid bearer token, when auth is on.                                                                                                             |
| `MissingBoardEnvelope` | `401`           | No `x-lakefront-board` header and the token doesn't pin exactly one board.                                                                                    |
| `Forbidden`            | `403`           | Token's claims don't allow the procedure's required verb on this board.                                                                                       |
| `InvalidInput`         | `400`           | Request body isn't valid JSON, or fails the procedure's input schema.                                                                                         |
| `UnknownProcedure`     | `404`           | `<procedure>` in `/rpc/<procedure>` isn't in the contract.                                                                                                    |
| `MalformedResponse`    | — (client-side) | The response body didn't decode into a valid envelope for the procedure that was called.                                                                      |
| `TransportFailed`      | — (client-side) | Every retry attempt failed at the transport level (network error, timeout, a non-JSON 5xx). See the retry policy in the [wire contract](/reference/contract). |

Full status/tag mapping and the envelope discipline that keeps a ran-but-failed handler at `200` are covered in the [wire protocol reference](/reference/wire-protocol).

## messageOf

```ts
function messageOf(cause: unknown): string;
```

Normalises a thrown value into a message without swallowing its type: `cause instanceof Error ? cause.message : String(cause)`. Exported from both `lakefront/contract` and `lakefront` (`core/errors.ts` re-exports the contract's copy so engine code keeps one import path).

## Fixing the actionable ones

- `ColumnTypeConflict`: a value doesn't fit the column's current type. Fix with `widenColumn(board, column, to)`, using the lowercase `ColumnType` vocabulary (`"text"`, `"bigint"`, `"double"`, …). See the [engine reference](/reference/engine) and [schema guide](/guides/schema).
- `LakeUnavailable`: the catalog Postgres or the lake storage is unreachable. See the [troubleshooting guide](/guides/troubleshooting).
