> Fetch the index of every Tonbo docs page as Markdown at https://tonbo.dev/llms.txt

# Errors

Last updated 29 October 2026

The error classes the package throws, each kind, when it happens and what to do.

Every class extends `UrsulaError` and is exported by `@tonbo/sqlite-ursula` and `/tonbo`; `TonboError` by `/tonbo` alone. Any other error, such as a constraint violation or a syntax error, is [node:sqlite](https://nodejs.org/docs/latest-v22.x/api/sqlite.html)’s, unchanged. Symptoms and their fixes are on [Troubleshooting](https://tonbo.dev/docs/databases/troubleshooting.md).

## UrsulaError

```text
class UrsulaError extends Error {
  readonly kind: UrsulaErrorKind;
  readonly action: "retry" | "reopen" | "stop" | "fix";
  readonly cause?: unknown;
  readonly code?: string;    // node:sqlite's, when cause is its error
  readonly errcode?: number;
  readonly errstr?: string;
}
```

`kind` names what failed and `action` what to do: branch on `action`. `cause` is the error the failure surfaced as, usually SQLite’s, whose `code`, `errcode` and `errstr` are copied, so code that reads them keeps working. Network failures never throw from statements or `synced()`: they wait.

```js
import { UrsulaError } from "@tonbo/sqlite-ursula/tonbo";

try {
  db.prepare("INSERT INTO notes(body) VALUES (?)").run(body);
} catch (error) {
  if (!(error instanceof UrsulaError)) throw error; // a constraint, a syntax error
  if (error.action === "retry") await db.synced(); // then run the transaction again
  else throw error; // reopen, stop or fix
}
```

### Actions

`UrsulaErrorAction`. The extension names each failure’s kind, action and class, the error to throw, through `ursula_classify` since 0.11.2, so a kind this page does not list, from a newer extension, carries the extension’s action; 0.11.1 said `fix` for such a kind. Messages are the extension’s too.

| Action | What to do |
| --- | --- |
| `retry` | Nothing was lost and the database stays usable. After `backlog_full`, await `synced()` and run the transaction again; after a failed `open()`, call it again later. |
| `reopen` | Close every connection of the database and `open()` it again. |
| `stop` | Another owner took the stream over. Stop using the database, and never reopen it in a loop: opening it takes the stream back. |
| `fix` | It cannot succeed as asked. Fix the URL, the setup, the stream, the key or the transaction, then open the database again. |

`UrsulaErrorKind` is every kind on this page: the union of `WriteRefusedKind`, `ResetKind`, `UnavailableKind`, `SetupKind` and `TonboKind`.

## UrsulaWriteRefusedError

A write or `COMMIT` refused with nothing committed: SQLite rolled the whole transaction back, and the database stays usable. Its `errcode` is 7178, `SQLITE_IOERR_AUTH`, and [status().refused](https://tonbo.dev/docs/reference/nodejs/status.md#field-refused) keeps the last one.

| Kind | Action | When | What to do |
| --- | --- | --- | --- |
| `backlog_full` | `retry` | More than `maxPendingBytes` of commits wait for the stream. | Await `synced()`, then run the transaction again. |
| `unsupported` | `fix` | A write the extension does not capture: in `EXCLUSIVE` locking mode, or a commit that leaves [WAL](https://sqlite.org/wal.html) mode or 4 KiB pages. | Leave journal_mode, the page size and the locking mode as open() sets them. |
| `too_large` | `fix` | The commit holds more bytes of pages than the commit limit (see [Limits](https://tonbo.dev/docs/databases/limits.md#transaction)). | Split the transaction, or raise `URSULA_VFS_MAX_COMMIT_BYTES`. |

## UrsulaResetError

The database was reset. Every statement of it throws this, reads included (`errcode` 266 for a read, 778 for a write), `synced()` rejects with it, and the [reset listeners](https://tonbo.dev/docs/reference/nodejs/database.md#reset-listener) get it. See [After a reset](https://tonbo.dev/docs/reference/nodejs/database.md#after-reset).

| Kind | Action | When | What to do |
| --- | --- | --- | --- |
| `fenced` | `stop` | Another process opened the database and took the stream over. | Stop. Find the second writer before you open the database again. |
| `stale` | `reopen` | Another owner wrote or claimed the stream since this cache's claim, found once a provisional open reached the stream, or the stream sat idle past the server's producer expiry. | Open it again: the new open takes over with the pending commits. |
| `rewound` | `reopen` | The stream's history moved back past commits this database holds, such as a restore to an earlier point. | Open it again; the old cache goes to `salvage/` when it holds data. |
| `deleted` | `fix` | The stream was deleted. | Create the database again, or stop. |
| `local_damage` | `reopen` | The host's disk failed the extension: a failed fsync, a lost WAL page or a log write it could not cut. | Check the disk, then open it again. |
| `stream_state` | `fix` | The stream is in a state replication cannot continue on. | Fix the stream, then open it again. |
| `internal` | `reopen` | An internal failure of the extension. | Open it again. |

## UrsulaUnavailableError

`open()` found no trusted state in time: its signal fired while the stream, or for an open by name the Tonbo API, did not answer as it must. `kind` is the last condition it met, or `cancelled` when it met none. A [token provider](https://tonbo.dev/docs/reference/nodejs/tokens.md#token-provider) that handed out no token in time rejects the open with `unavailable`, its last error as the `cause`. From [importSqlite()](https://tonbo.dev/docs/reference/nodejs/gate-and-import.md#import-sqlite) every kind says `fix`, since the stream may hold the import’s claim.

| Kind | Action | When | What to do |
| --- | --- | --- | --- |
| `unreachable` | `retry` | No connection to the stream or the Tonbo API. | Check the network, then retry. |
| `unavailable` | `retry` | The server answered 5xx or no definite answer, a database was still being created, or a token provider handed out no token. | Retry later. |
| `rate_limited` | `retry` | The server answered 429. | Retry later. |
| `unauthorized` | `fix` | The stream answered 401: the token is missing, expired or revoked. | Fix the token or the key. After a token provider’s failed call the action is `retry`, unless it failed for good. |
| `payment_required` | `fix` | The stream answered 402. | See [When the balance runs out](https://tonbo.dev/docs/databases/pricing.md#zero-balance). |
| `forbidden` | `fix` | The stream answered 403. | Check the key's scope and the account's access. |
| `cancelled` | `retry` | The signal fired before the open met any condition. | Retry with a longer signal. |

`UnavailableKind` also names `wrong_cluster`, but the package throws it as an [UrsulaSetupError](#ursula-setup-error).

## UrsulaSetupError

`open()`, `resolveDatabase()` or `importSqlite()` cannot succeed as asked, or a database that retired was asked for `synced()` or `status()`. Its message says what to change.

| Kind | Action | When | What to do |
| --- | --- | --- | --- |
| `invalid_argument` | `fix` | An option, a file to import, or an answer of the Tonbo API the package cannot use, or an open by name with a second API key while the first key’s tokens serve (since 0.11.2). The message names which. | Fix what it names. For a second key, use one key per process (see [One token per process](https://tonbo.dev/docs/reference/nodejs/tokens.md#one-token)). |
| `invalid_url` | `fix` | Not a stream URL, or one that would get the API key’s tokens over plain `http://` to a host other than loopback. | Use the database’s `https://` URL. |
| `invalid_name` | `fix` | An empty database name (since 0.11.1). | Pass the name (see [Names](https://tonbo.dev/docs/databases/manage.md#names)). |
| `not_found` | `fix` | The stream is missing. Over a cache that holds data, only `recreate` opens it. | Create the database (see [Manage databases](https://tonbo.dev/docs/databases/manage.md)). |
| `in_use` | `retry` | Another process on this host holds the stream's cache, or its binding under another cache root of this process still drains. | Retry once the other one has let go. |
| `bound_elsewhere` | `fix` | The stream is open in this process under another cache root, with connections open. | Close it there first, or pass its `dir`. |
| `local_setup` | `fix` | The cache root, a file in it, a file to import or the login file cannot be read or written, or the extension library is older than the package, which [loadUrsulaVfs()](https://tonbo.dev/docs/reference/nodejs/sql-functions.md#load-ursula-vfs) refuses since 0.11.2. | Fix the path or its permissions, or load the library of the package’s release (see [SQLITE_URSULA_VFS](https://tonbo.dev/docs/reference/nodejs/environment.md#sqlite-ursula-vfs)). |
| `stream_format` | `fix` | The stream holds another content type: it is not a database's stream. | Check the URL. |
| `no_server_identity` | `fix` | An answer without `Ursula-Protocol` and `Ursula-Cluster-Id`, such as a proxy’s. | Check the URL and anything in front of the server. |
| `protocol_mismatch` | `fix` | A server of another release line. | Use a package release of the server’s line: the exact version these docs name, for Tonbo. |
| `wrong_cluster` | `fix` | The cache was built on another deployment than the server's. | Pass another `dir`, or remove that stream’s cache. |
| `stream_state` | `fix` | The stream refuses opens: an import runs on it, or one failed after it claimed the stream. | Wait for the import, or delete and create the database again. |
| `claim_races` | `retry` | The claim lost to other processes claiming the stream at the same time. | Make sure one process opens it, then retry. |
| `not_bound` | `fix` | The database retired: its last connection closed. | Open it again with `open()`. |
| `internal` | `reopen` | An internal failure of the extension. | Open it again. |

## TonboError

From `/tonbo`, since 0.11.1. A key, a login or a database to fix: the action is always `fix`. `fix` holds what to do, the variable to set or the command to run, and the message ends with it. `requestId` is the Tonbo API’s id of the answer that failed, when one did; quote it to support.

```text
class TonboError extends UrsulaError {
  readonly kind: TonboKind;
  readonly fix: string;
  readonly requestId: string | undefined;
}
```

| Kind | When | fix |
| --- | --- | --- |
| `missing_credentials` | No `apiKey`, no `TONBO_API_KEY` and no login file. | `Set TONBO_API_KEY, or run: tonbo login` |
| `credentials_refused` | The login file is a symlink, not a regular file, or open to group or others. It is never read. | `Run: chmod 600 <path>`, or replace the file |
| `invalid_api_key` | The API refused the key (`401`), the token endpoint refused it a token, the key cannot read the database (`403`), `TONBO_API_KEY` holds no usable key, or the login expired or is not one this package reads. | Pass a valid `apiKey`, set `TONBO_API_KEY`, or `Run: tonbo login`, after the key’s source; for a `403`, `Use an API key that can read database "<name>"` |
| `database_not_found` | The key's account has no database of that name, or it is being deleted. | `Run: tonbo database create <name>` |
