Error codes
Every error smaltdb raises carries a code. In the client it is a SmaltdbError; in the engine it is a SmaltError or a subclass. Codes are strings that start with SMALT_. Where SQLite has a matching result code, the name follows it (SQLITE_BUSY → SMALT_BUSY).
The errors guide shows how to handle them. This page lists every code the engine and client raise, what causes it, and what to do.
| Code | Meaning | Retry? |
|---|---|---|
SMALT_ERROR | General SQL error: unknown table or column, misuse of a statement, bad data in an import | No |
SMALT_SYNTAX | The SQL does not parse | No |
SMALT_UNSUPPORTED | Valid SQL that smaltdb does not support | No |
SMALT_CONSTRAINT_NOTNULL | NULL in a NOT NULL column | No |
SMALT_CONSTRAINT_UNIQUE | Duplicate value for a UNIQUE constraint or index | No |
SMALT_CONSTRAINT_PRIMARYKEY | Duplicate primary key | No |
SMALT_CONSTRAINT_CHECK | A CHECK constraint failed | No |
SMALT_CONSTRAINT_FOREIGNKEY | A foreign key constraint failed | No |
SMALT_CONSTRAINT_DATATYPE | A value cannot be stored in the column's type | No |
SMALT_MISMATCH | A value of the wrong kind where a specific kind is required | No |
SMALT_RANGE | Wrong number of parameters, or a missing parameter | No |
SMALT_TOOBIG | An index key is too large | No |
SMALT_FULL | No more rowids | No |
SMALT_BUSY | The database is in use in a way that blocks the request | After the blocker ends |
SMALT_INTERRUPT | The request was cancelled | Your choice |
SMALT_RETRY | The tab that owned the database changed | Yes |
SMALT_MISUSE | The API was used incorrectly (closed database, finalized statement) | No |
SMALT_PLUGIN_MISSING | A feature needs a plugin the engine does not have | After installing it |
SMALT_EXTERNAL | Reading external data failed | Often |
SMALT_ASYNC_REQUIRED | A synchronous engine call needs external data | Use the async method |
SMALT_CANTOPEN | A database file cannot be opened | No |
SMALT_NOTADB | The file is not a smaltdb database | No |
SMALT_FORMAT | The file has an unsupported format version | No |
SMALT_CORRUPT | The database file is damaged | No |
SMALT_READONLY | A write on a read-only snapshot | No |
SMALT_WORKER | The engine worker failed | Reopen |
SMALT_INTERNAL | An unexpected error inside the engine | No |
Unless noted, a statement that fails changes nothing: it is rolled back on its own, and an open transaction stays open.
SMALT_ERROR
The general error for SQL that refers to things that do not exist or uses them wrongly. The message says what is wrong. Common causes:
- an unknown table, column, function, view, index, savepoint or connection;
- an object that already exists (
CREATE TABLEwithoutIF NOT EXISTS); - transaction control out of place:
BEGINinside a transaction,COMMITorROLLBACKwithout one; - a
db.transaction()callback that ranROLLBACKitself (the transaction was rolled back inside the transaction callback); - a query that is valid syntax but not valid SQL (an aggregate in
GROUP BY, a compoundSELECTwhose sides have different column counts, an ambiguous column name); - an integer overflow in arithmetic or
sum(); - bad import data: malformed CSV or JSON, a JSON item that is not an object or array (or does not match the first item's kind), a wrong field count, an unknown column name.
Remedy: fix the SQL or the data.
SELECT * FROM no_such_table;SMALT_SYNTAX
The SQL does not parse. The message gives the token and position: SELEC 1 fails with near "SELEC": syntax error (line 1, column 1). query() and prepare() accept one statement only; a second statement is reported as a syntax error. (exec() with parameters reports it as SMALT_MISUSE.)
Remedy: fix the SQL. See the SQL reference.
SMALT_UNSUPPORTED
The SQL is valid in SQLite, or reserved for a future version, but smaltdb does not support it. Examples: WITHOUT ROWID tables, generated columns, CREATE TABLE … AS SELECT, some PRAGMAs, UPDATE … ORDER BY/LIMIT, indexes on columnar tables. The message names the feature.
Remedy: rewrite the statement without the feature. See differences from SQLite.
CREATE TABLE w(a INTEGER PRIMARY KEY) WITHOUT ROWID;SMALT_CONSTRAINT_NOTNULL
An INSERT or UPDATE would store NULL in a NOT NULL column. Message: NOT NULL constraint failed: table.column.
Remedy: provide a value, or give the column a DEFAULT.
CREATE TABLE users(id INTEGER PRIMARY KEY, email TEXT NOT NULL UNIQUE, age INTEGER CHECK (age >= 0));
INSERT INTO users(email) VALUES (NULL);SMALT_CONSTRAINT_UNIQUE
An INSERT or UPDATE would create a second row with the same value in a UNIQUE column or unique index. Message: UNIQUE constraint failed: table.column.
Remedy: use a different value, or turn the insert into an upsert (ON CONFLICT … DO UPDATE or DO NOTHING, see INSERT).
INSERT INTO users(email) VALUES ('a@example.com'), ('a@example.com');SMALT_CONSTRAINT_PRIMARYKEY
A row with the same primary key exists already. The message has the same form as for UNIQUE: UNIQUE constraint failed: table.column.
Remedy: as for SMALT_CONSTRAINT_UNIQUE.
INSERT INTO users(id, email) VALUES (1, 'b@example.com'), (1, 'c@example.com');SMALT_CONSTRAINT_CHECK
A CHECK constraint is false for the new row. Message: CHECK constraint failed: <constraint>.
Remedy: fix the data.
INSERT INTO users(email, age) VALUES ('d@example.com', -1);SMALT_CONSTRAINT_FOREIGNKEY
A row refers to a parent row that does not exist, or a parent row that is still referenced would be deleted or changed. Message: FOREIGN KEY constraint failed. For deferred foreign keys, the error comes from COMMIT, and the transaction stays open so you can fix the data.
Remedy: insert the parent first, delete children first, or use an ON DELETE action. See tables and schema.
CREATE TABLE orders(id INTEGER PRIMARY KEY, user_id INTEGER REFERENCES users(id));
INSERT INTO orders(user_id) VALUES (999);SMALT_CONSTRAINT_DATATYPE
A value cannot be converted to the column's declared type. Tables are strictly typed, so 'abc' cannot go into an INTEGER column. Message: cannot store TEXT value in INTEGER column table.column.
Remedy: convert the value first (CAST, or in JavaScript), or declare the column ANY. See data types.
INSERT INTO users(email, age) VALUES ('e@example.com', 'abc');SMALT_MISMATCH
A value of the wrong kind where one kind is required. Message: datatype mismatch, or more specific. Causes:
LIMITorOFFSETthat is NULL or not an integer;- a rowid (
INTEGER PRIMARY KEY) value that is not an integer; - a parameter of an unsupported JavaScript type, such as a
Dateor anArrayBuffer(unsupported parameter type); - a materialized view whose query produced a value that does not fit a view column.
Remedy: pass a value of the right type. See values.
SELECT 1 LIMIT NULL;SMALT_RANGE
The parameters do not match the statement:
- an array with the wrong number of values (
statement expects 2 parameter(s), got 1); - no parameters for a statement that has placeholders (
statement expects 1 parameter(s)); - an object without a key for a named parameter (
missing value for parameter :id); - an object for a statement that has
?or?NNNplaceholders (statement has positional parameters (? or ?NNN): bind them with an array, not an object); - a
bigintoutside the 64-bit INTEGER range (parameter ?1: integer 9223372036854775808 is out of range (INTEGER values are 64-bit signed)).
Also raised by the CSV import when delimiter or quote is not a single character.
Remedy: pass one value per placeholder, within range. See parameters.
SELECT ?;SMALT_TOOBIG
A value is too large to be stored in an index: index keys must fit within a page. Message: index key too large for index <name>. The limit depends on the page size.
Remedy: do not index long text or blobs. Index a prefix or a hash instead, for example an index on an expression, or open the database with a larger pageSize.
CREATE TABLE docs(body TEXT);
CREATE INDEX docs_body ON docs(body);
INSERT INTO docs VALUES (hex(zeroblob(10000)));SMALT_FULL
A new row needs a rowid, the largest rowid in the table is already the largest 64-bit integer, and no free one was found: as in SQLite, 100 random candidates were all taken, or the table is AUTOINCREMENT (which never picks random rowids). Message: database or disk is full (rowid space exhausted).
Remedy: do not store values near 2^63 − 1 in an INTEGER PRIMARY KEY; use a separate column for them.
CREATE TABLE seq(id INTEGER PRIMARY KEY AUTOINCREMENT, v TEXT);
INSERT INTO seq VALUES (9223372036854775807, 'max');
INSERT INTO seq(v) VALUES ('next');SMALT_BUSY
The request cannot run now because of something else holding the database:
| Message | Cause | Remedy |
|---|---|---|
cannot modify the database while a query cursor is open; finish or close it first | A write from a tab that has an open stream on the owning engine worker. In the engine, a write while a RowCursor is open. | Finish or close the stream (or cursor), then write. |
cannot lock <name>.db: … | Another engine worker has the OPFS file open: another tab or open() call, with multi-tab access off or unavailable. | Close the other database, or use multi-tab access. |
cannot checkpoint inside a transaction | Engine checkpoint() called inside a transaction. | Call it after COMMIT. |
SMALT_INTERRUPT
The request was cancelled through its AbortSignal (message interrupted), or the fetch of external data it was waiting for was aborted. A statement that was interrupted while running has been rolled back. See cancellation.
Remedy: none needed. Run the request again if you still want its result.
SMALT_RETRY
Client only, with multi-tab access. The tab whose engine worker owned the database closed, and another took over:
- requests that were in flight are lost (message
the tab that owned the database closed; retry); - a
PreparedStatementprepared before the change cannot be used (messagethe tab that owned the database changed; prepare the statement again).
An open transaction of this tab is gone too.
Remedy: retry the request, or prepare the statement again. A write that was in flight may have committed before the owner closed: make retried writes safe to repeat. See retrying.
SMALT_MISUSE
The API was used in a way that cannot work:
| Message | Cause |
|---|---|
database is closed | A call on a closed Database (client or engine). |
statement is finalized | A call on a PreparedStatement after finalize(). |
parameters can only be bound with a single statement | exec (client or engine) or engine execAsync with parameters and several statements. |
the transaction has ended: use tx only while the transaction callback runs | A method of a db.transaction() handle (tx) called after its callback finished. |
the statement was prepared on another Database | tx.statement(stmt) with a statement of another Database. |
invalid page size … | A pageSize that is not a power of two from 512 to 65536. |
plugin … needs plugin API version …; this engine implements version 1 | A plugin written for another version of the plugin interface. |
plugin … is installed twice | The same plugin listed twice in plugins. |
plugin …: format 'csv' is already defined (or table function, source prefix, connection type, method) | Two plugins register the same name. |
plugin … has no method … | callPlugin with a method the plugin does not have. |
serve() was already called in this worker | A second serve() call in an engine worker entry. |
Low-level engine classes (pager, b-tree, VFS) raise it for invalid calls as well.
Remedy: fix the calling code.
SMALT_PLUGIN_MISSING
A statement or a plugin call needs a plugin the engine does not have. The message names smaltdb's plugin for the feature when there is one:
- a format:
read_parquet('file:x.parquet'): the parquet format needs the @smaltdb/parquet plugin (install it and add it to the engine's plugins); - a source:
read_csv('file:x.csv'): reading file:x.csv needs the @smaltdb/external plugin (…), orreading the path 'data/x.csv'for a local path; - a connection type, in
CREATE CONNECTIONor when a statement reads through the connection:connection c: an s3 connection needs the @smaltdb/external plugin (…); - a plugin helper:
importCSV(): import and export needs the @smaltdb/io plugin (…),registerFile(): registering files and credentials needs the @smaltdb/external plugin (…); - a format no plugin is known for:
read_csv('file:x.dat'): no installed plugin provides the 'xml' format (installed: csv, json, jsonl, ndjson, tsv).
It is raised when a statement is planned or run, or when a plugin method is called, never when a database is opened. External tables and connections created with a plugin stay in the catalog without it.
Remedy: install the plugin and add it to the engine: serve({ plugins: [...] }) in the engine worker entry, with worker: () => new Worker(new URL('./db-worker.ts', import.meta.url), { type: 'module' }) in open(), or Database.openMemory({ plugins }) / Database.open(…, { plugins }) with smaltdb/engine. See plugins.
SMALT_EXTERNAL
Reading external data failed. Plugins raise it: a format or source plugin that throws a plain Error is reported as SMALT_EXTERNAL (see Writing plugins). The message starts with the source (read_csv('file:x.csv'): …, a URL, an external table) and says why: no file registered under that name, an HTTP error, a network failure, a file that does not parse, a missing optional package (this file uses a codec that needs the hyparquet-compressors package), or credentials that could not be obtained (a credentials callback that failed, did not answer within 30 seconds, or whose tab closed).
Remedy: depends on the cause. Network and HTTP errors are often temporary and can be retried.
SELECT * FROM read_csv('file:not-registered.csv');SMALT_ASYNC_REQUIRED
Engine only. A synchronous call (query, run, exec, Statement.run …) was given a statement that reads external data, which must be fetched first, or REFRESH MATERIALIZED VIEW … IF CHANGED, which checks its sources first. The client never raises it: the engine worker uses the async path.
Remedy: use queryAsync, runAsync or execAsync. See the engine API.
SMALT_CANTOPEN
A file cannot be opened: OPFS refused to open or create it, OPFS sync access handles do not exist where the engine runs (engine OpfsVfs in Node, on a page's main thread, or in a browser without OPFS), or a file that must exist does not (engine MemoryVfs.open with create: false, a parallel reader opening a database that was never written).
Remedy: check that OPFS is available and the storage quota is not exhausted.
SMALT_NOTADB
The file exists but does not start with the smaltdb file header. smaltdb does not read SQLite database files.
Remedy: open a different name, or delete the file.
SMALT_FORMAT
The file was written by a version of smaltdb with a different major format version, which this version cannot read. Message: unsupported format version N.x (engine supports M.x).
Remedy: open it with a matching smaltdb version and export the data.
SMALT_CORRUPT
The database file is damaged: a page checksum does not match, a page or record has an impossible layout, or an index points at a row that does not exist. Page checksums are checked on every read from storage, so damage is reported when it is read, not ignored.
Remedy: restore from a backup or export what can still be read. PRAGMA integrity_check lists problems it can find.
SMALT_READONLY
A write reached a read-only snapshot (attempt to write a read-only snapshot). Only parallel reader workers open read-only snapshots, and they are only given read-only queries, so applications should not see this code.
SMALT_WORKER
Client only. The engine worker raised an error event: it failed to load (a wrong worker URL, a script that is not the smaltdb worker, a blocked module worker) or crashed. All pending requests reject with this code.
Remedy: check the browser console and the worker setup. Open the database again.
SMALT_INTERNAL
The engine worker caught an error that is not a smaltdb error, such as a TypeError from a bug or a browser API failure, or a plain Error thrown by a plugin method. The message is the original error's message (or its name when the message is empty).
Remedy: report it, with the message and the SQL, at the project's issue tracker.