Errors

Last updated 3 November 2026

View .md

The error classes the package raises, each kind with its action, when it happens and what to do.

Every class derives from UrsulaError and is exported by sqlite_ursula. Which failure is which is the SQLite extension’s to say, so a kind this page does not list, from a newer extension, carries the extension’s own action. Any other error, such as a constraint violation or a syntax error, is sqlite3’s, unchanged. Symptoms and their fixes are on Troubleshooting.

UrsulaError

class UrsulaError(Exception):
    kind: str
    action: Action
    fix: str | None
    sqlite_errorcode: int | None
    sqlite_errorname: str | None
    def __init__(
        self,
        message: str,
        kind: str,
        action: Action,
        *,
        fix: str | None = None,
        cause: BaseException | None = None,
    ) -> None: ...
import sqlite_ursula

try:
    db.execute("INSERT INTO notes(body) VALUES (?)", (body,))
    db.commit()
except sqlite_ursula.UrsulaError as error:
    if error.action == "retry":
        db.synced()  # then run the transaction again
    else:
        raise  # reopen, stop or fix

kind names what failed and action what to do. Network failures never raise from statements or synced(): they wait. An error that replaced a SQLite error keeps it as __cause__. Errors pickle across processes, as multiprocessing needs.

FieldTypeWhat it holds
kindstrWhat failed: one of the kinds below, or one a newer extension names.
actionActionWhat to do: an Action. Branch on it.
fixstr | NoneHow to fix a host the package cannot run on, for host_unsupported; None otherwise.
sqlite_errorcodeint | NoneThe SQLite error code of the error it replaced, as sqlite3.Error carries it, or None.
sqlite_errornamestr | NoneIts name, such as SQLITE_IOERR_AUTH, or None.

The package raises these itself; construct one only to test your handling:

ParameterTypeDefaultWhat it does
messagestrRequiredThe message, the extension's or the package's.
kindstrRequiredWhat failed: one of the kinds below.
actionActionRequiredWhat to do: an Action.
fixstr | NoneNoneHow to fix a host the package cannot run on, for host_unsupported.
causeBaseException | NoneNoneThe error it replaces, kept as __cause__; its sqlite_errorcode and sqlite_errorname are copied.

Action

Action is Literal["retry", "reopen", "stop", "fix"]:

ActionWhat to do
retryNothing was lost and the database stays usable. After backlog_full, call synced() and run the transaction again; after a failed open(), call it again later.
reopenClose every connection of the database and open() it again.
stopAnother writer took the database over. Stop using it, and never reopen it in a loop: opening it takes it back.
fixIt cannot succeed as asked. Fix the URL, the host, the token, the database or the transaction, then open it again.

UrsulaWriteRefusedError

class UrsulaWriteRefusedError(UrsulaError, sqlite3.OperationalError):
    ...

A write or COMMIT refused with nothing committed: SQLite rolled the whole transaction back, and the database stays usable. Its sqlite_errorcode is 7178, SQLITE_IOERR_AUTH. It is also a sqlite3.OperationalError, so an except sqlite3.OperationalError clause keeps catching it.

KindActionWhenWhat to do
backlog_fullretryMore than max_pending_bytes of commits wait for Tonbo, such as during an outage.Call db.synced(), then run the transaction again. See Troubleshooting.
unsupportedfixA write the database does not capture: one in EXCLUSIVE locking mode, or a commit that leaves WAL mode or 4 KiB pages.Leave journal_mode, the page size and the locking mode as open() sets them.
too_largefixThe transaction’s commit holds more bytes of pages than the commit limit (see Limits).Split the transaction, or raise URSULA_VFS_MAX_COMMIT_BYTES. See Troubleshooting.

UrsulaResetError

class UrsulaResetError(UrsulaError, sqlite3.OperationalError):
    ...

The database was reset. Every statement of it raises this, reads included, synced() raises it, and the on_reset callbacks get it. It is also a sqlite3.OperationalError.

KindActionWhenWhat to do
fencedstopAnother process opened the database and took it over.Stop using it, and find the second writer before you open it again. Never reopen it in a loop: each open takes the database back. See Troubleshooting.
stalereopenAnother owner wrote or claimed the database since this cache's claim, or it sat idle past the server's producer expiry.Close every connection of it and open it again: the new open takes over with the pending commits. See Troubleshooting.
rewoundreopenThe database's history moved back past commits this cache holds, such as a restore to an earlier point.Open it again; the old cache goes to salvage/ when it holds data. See Troubleshooting.
deletedfixThe database was deleted.Create it again (see Manage databases), or stop. See Troubleshooting.
local_damagereopenThe host's disk failed: a failed fsync, a lost WAL page or a log write that could not be cut.Check the disk, then open it again. See Troubleshooting.
stream_statefixThe database's stream is in a state replication cannot continue on.Fix the stream, then open the database again.
internalreopenAn internal failure of the extension.Open it again.

UrsulaUnavailableError

class UrsulaUnavailableError(UrsulaError):
    ...

open() or aopen() found no trusted state before its timeout: kind is the last condition it met, or cancelled when it met none. From import_sqlite() every kind says fix, since the database may hold the import’s claim.

KindActionWhenWhat to do
unreachableretryNo connection to Tonbo.Check the network, then call open() again. See Troubleshooting.
unavailableretryTonbo answered 5xx or no definite answer, or the database was still being created.Call open() again later. See Troubleshooting.
rate_limitedretryTonbo answered 429.Call open() again later.
unauthorizedfixTonbo answered 401: the token is missing, expired or revoked, or is a key that acts as an Agent and was not exchanged.Pass a valid key or access token, and call set_token() with each new one. See Troubleshooting.
payment_requiredfixTonbo answered 402.See When the balance runs out. See Troubleshooting.
forbiddenfixTonbo answered 403: the key does not reach this database, or is read-only and the open claims it.Check what the key reaches (see What a key can reach), or open with read_only=True. See Troubleshooting.
cancelledretryThe timeout passed, or the open was cancelled, before it met any condition.Call open() again, with a longer timeout.

UrsulaSetupError

class UrsulaSetupError(UrsulaError):
    ...

It cannot succeed as asked: the message says what to change. Raised by an open, an import, set_token(), a gate, a LangGraph class, and by a database that retired or a process that forked.

KindActionWhenWhat to do
invalid_argumentfixAn argument the package cannot take: a value of the wrong type or range, autocommit=False, database or uri for sqlite3.connect, read_only with recreate, a connection open() did not make, or a file to import. The message names which.Fix what the message names.
invalid_urlfixNot a database's URL.Use the database’s https:// URL, as tonbo database show --json prints it.
not_foundfixThe database does not exist. Over a cache that holds data, only recreate opens it.Create the database (see Manage databases), wait until it is active, and open it again. See Troubleshooting.
in_useretryAnother process on this host holds the database's cache, or its binding under another dir of this process still drains.Call open() again once the other one has let go.
bound_elsewherefixThe database is open in this process under another dir, with connections open.Close it there first, or pass the same dir.
local_setupfixThe cache directory, a file in it or a file to import cannot be read or written, or SQLite could not load the extension library, or the library is older than the package.Fix the path or its permissions, or load the library of the package’s release (see SQLITE_URSULA_VFS).
stream_formatfixThe URL names a stream that is not a database's.Check the URL.
no_server_identityfixAn answer that did not come from Tonbo, such as a proxy's.Check the URL and anything in front of it.
protocol_mismatchfixA server of another release line.Install the exact release these docs name.
wrong_clusterfixThe cache was built on another deployment than the one that answers.Pass another dir, or remove that database’s cache.
stream_statefixThe database refuses opens: an import runs on it, or one failed after it claimed it.Wait for the import, or delete and create the database again.
claim_racesretryThe claim lost to other processes opening the database at the same time.Make sure one process opens it, then call open() again.
not_boundfixThe database retired: its last connection closed.Open it again with open().
host_unsupportedfixThis Python cannot run the package: its sqlite3 cannot load extensions or runs too old a SQLite, or the platform has no prebuilt library.Use the Python the error’s fix names; python -m sqlite_ursula checks one (see Pythons that run it).
forkedfixA call in a child process forked after its parent opened a database or imported one.Open databases after the fork, or use the spawn or forkserver start method.
internalreopenAn internal failure of the extension or of the package's events thread.Open the database again; restart the process if it repeats.

Other errors

  • TimeoutError from synced(), asynced(), gate() and agate() past their timeout, while the commits keep replicating.
  • asyncio.CancelledError from an aopen(), asynced() or agate() you cancelled. A cancelled aopen() cancels its bind too.
  • KeyboardInterrupt from an open() or synced() that Ctrl-C interrupted.
  • sqlite3.Error for everything else a statement raises, unchanged.

A library may wrap an error: gate() finds an UrsulaError in __cause__, SQLAlchemy’s .orig and an ExceptionGroup, so look there too before you branch on action.