tursodatabase/turso · error · DatabaseError
batch statement ${index} failed: ${message}
Error message
batch statement ${index} failed: ${message} What it means
In the serverless JS client's session.batch(), each statement's args are encoded client-side via encodeSqlArgs before anything is sent. If encoding throws (unsupported value types), the error is rethrown as batchInputError(index, message) with the statement's zero-based index; since nothing has executed, batchResults in the resulting DatabaseError is empty.
Source
Thrown at serverless/javascript/src/session.ts:521
mode?: BatchMode,
queryOptions?: QueryOptions,
safeIntegers: boolean = false,
raw: boolean = false,
): Promise<any> {
const userSteps: BatchStep[] = statements.map((statement, index) => {
if (typeof statement === 'string') {
return {
stmt: { sql: statement, args: [], named_args: [], want_rows: true },
};
}
// A value that cannot be encoded fails client-side before anything
// is sent; report it with the statement's index like any other
// statement failure. Nothing has executed, so batchResults is empty.
let encodedArgs;
try {
encodedArgs = encodeSqlArgs(statement.args ?? []);
} catch (e: any) {
throw batchInputError(index, e?.message ?? String(e));
}
return {
stmt: {
sql: statement.sql,
args: encodedArgs.args,
named_args: encodedArgs.namedArgs,
want_rows: true,
},
};
});
if (mode !== undefined) {
for (let index = 0; index < statements.length; index++) {
const statement = statements[index];
const sql = typeof statement === 'string' ? statement : statement.sql;
const keyword = firstSqlKeyword(sql);
if (keyword !== undefined && TRANSACTION_CONTROL_KEYWORDS.has(keyword)) {
throw batchInputError(index, `${keyword} is not allowed in an atomic batch`);View on GitHub (pinned to c1e5928725)
Solutions
- Use the batchIndex on the thrown DatabaseError to find the offending statement and fix its args.
- Convert values to supported types: null, number, string, bigint (i64 range), or Uint8Array.
- Replace undefined with explicit null; serialize Dates/objects to strings/JSON before batching.
- Encode args once yourself with the same encoder to validate before calling batch().
Example fix
// before
await session.batch([{ sql: "INSERT INTO t VALUES (?)", args: [meta.tags] }]); // undefined
// after
await session.batch([{ sql: "INSERT INTO t VALUES (?)", args: [meta.tags ?? null] }]); Defensive patterns
Strategy: validation
Validate before calling
function isEncodable(v) {
return v === null || typeof v === 'number' || typeof v === 'string' ||
(typeof v === 'bigint' && v >= -(2n**63n) && v < 2n**63n) ||
v instanceof Uint8Array;
}
statements.forEach((s, i) => (s.args ?? []).forEach(v => {
if (!isEncodable(v)) throw new Error(`statement ${i}: unencodable arg ${v}`);
})); Type guard
const isEncodable = (v: unknown): v is null | string | number | bigint | Uint8Array => v === null || typeof v === 'number' || typeof v === 'string' || (typeof v === 'bigint' && v >= -(2n**63n) && v < 2n**63n) || v instanceof Uint8Array;
Try / catch
try {
await session.batch(statements, mode);
} catch (e) {
if (e?.batchIndex !== undefined) {
console.error(`statement ${e.batchIndex} failed`, e.message, e.batchResults);
} else throw e;
} Prevention
- Replace undefined args with explicit null before batching
- Serialize Dates/objects to strings/JSON at the boundary
- Keep args as arrays or named records matching the encoder's expectations
- Validate BigInt range and binary types before batch()
When it happens
Trigger: Calling session.batch() with a statement whose args array or named-args record contains values encodeSqlArgs cannot serialize: undefined, unsupported objects, invalid BigInt, wrong container type.
Common situations: Optional JS fields left undefined; sending class instances/Dates instead of primitives; passing null-like placeholder values the encoder rejects; refactored code where args switched between array and named-object form.
Related errors
- batch statement ${index} failed: ${message}
- batch statement ${index} failed: ${keyword} is not allowed i
- BigInt value is outside SQLite's signed 64-bit integer range
- missing batch result in pipeline response
- expected batch result in pipeline response, got ${first.resp
AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-31).
Data as JSON: /api/errors/69447e514cbe163c.
Report an issue: GitHub.