tursodatabase/turso · error · TypeError
Expected first argument to be an array of statements
Error message
Expected first argument to be an array of statements
What it means
Connection.batch() validates its first argument with Array.isArray and throws a plain TypeError when it is not an array. Notably this check runs before the isOpen check, so a non-array argument raises this TypeError even on a closed connection. It mirrors the compat layer and better-sqlite3 convention that batch takes only arrays of statements.
Source
Thrown at serverless/javascript/src/connection.ts:316
*
* @example
* // Atomic via the mode parameter.
* await db.batch([
* { sql: "INSERT INTO users(name) VALUES (?)", args: ["Eve"] },
* { sql: "INSERT INTO users(name) VALUES (?)", args: ["Frank"] },
* ], "immediate");
*
* @example
* // Atomic via the transactionAsync() API for mixed workloads.
* const txn = db.transactionAsync(async (tx) => {
* await tx.batch([{ sql: "INSERT INTO users(name) VALUES (?)", args: ["Eve"] }]);
* await tx.run("UPDATE counters SET n = n + 1");
* });
* await txn.immediate();
*/
async batch(statements: BatchStatement[], options?: BatchMode | BatchOptions, queryOptions?: QueryOptions): Promise<any> {
if (!Array.isArray(statements)) {
throw new TypeError("Expected first argument to be an array of statements");
}
if (!this.isOpen) {
throw new TypeError("The database connection is not open");
}
await this.execLock.acquire();
try {
const { mode, raw } = normalizeBatchOptions(options);
// Inside an outer transaction(...) callback the surrounding BEGIN
// already opened a transaction on this stream; emitting another
// `BEGIN` step would fail, so ignore the user-supplied mode.
const effectiveMode = this.session.inTransaction ? undefined : mode;
const results = await this.session.batch(
statements,
effectiveMode,
queryOptions,
this.defaultSafeIntegerMode,
raw,
);View on GitHub (pinned to bad083fafb)
Solutions
- Pass an array: db.batch([{ sql: 'INSERT INTO t VALUES (?)', args: [1] }], 'write')
- Normalize: db.batch(Array.isArray(stmts) ? stmts : [stmts])
- Type the variable as BatchStatement[] to catch it at compile time
- Await async statement builders before passing their result
Example fix
// before
await db.batch({ sql: 'INSERT INTO t VALUES (?)', args: [1] });
// TypeError: Expected first argument to be an array of statements
// after
await db.batch([{ sql: 'INSERT INTO t VALUES (?)', args: [1] }]); Defensive patterns
Strategy: type-guard
Validate before calling
function toBatchStatements(v: BatchStatement[] | BatchStatement | undefined): BatchStatement[] {
if (v === undefined) return [];
return Array.isArray(v) ? v : [v];
}
await db.batch(toBatchStatements(stmts), 'write'); Type guard
function isBatchStatementArray(v: unknown): v is BatchStatement[] {
return Array.isArray(v) && v.every((s) => typeof s === 'string' || (typeof s === 'object' && s !== null && 'sql' in s));
} Try / catch
try {
await db.batch(stmts);
} catch (e) {
if (e instanceof TypeError && e.message.includes('array of statements')) {
throw new TypeError('db.batch expects an array — got ' + typeof stmts);
}
throw e;
} Prevention
- Type batch inputs as BatchStatement[] everywhere they are built
- Wrap single statements in brackets when converting sequential run() calls to a batch
- Await async statement builders before passing results to batch()
- Test batch construction helpers with empty and single-statement cases
When it happens
Trigger: db.batch({ sql: 'SELECT 1' }) with a bare object; db.batch(undefined) when a statement-building function returns nothing; db.batch(promisedArray) without await; passing a SQL string directly.
Common situations: Converting sequential run() calls into a batch and forgetting the array literal; conditional statement lists that collapse to undefined; JS code without TypeScript catching the shape; spread of a non-iterable into the argument.
Related errors
- Expected first argument to be an array of statements
- Expected first argument to be a function
- addBatch(String) cannot be called on a PreparedStatement
- Expected first argument to be an array of statements
- retryFetch: attempts must be a finite integer >= 1, got ${at
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/a830b5616339b846.
Report an issue: GitHub.