tursodatabase/turso · error · DatabaseError
batch statement ${index} failed: ${keyword} is not allowed i
Error message
batch statement ${index} failed: ${keyword} is not allowed in an atomic batch What it means
session.batch() with an explicit BatchMode runs atomically: the library emits its own BEGIN <mode> ... COMMIT ... ROLLBACK steps. Statements starting with a transaction-control keyword (BEGIN, COMMIT, ROLLBACK, SAVEPOINT, RELEASE, END) would corrupt that structure, so they are rejected before the request is sent, reported as batchInputError(index, ...) naming the keyword.
Source
Thrown at serverless/javascript/src/session.ts:539
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`);
}
}
}
let steps: BatchStep[];
let firstUserStepIdx = 0;
let beginIdx = -1;
let commitIdx = -1;
let rollbackIdx = -1;
if (mode === undefined) {
// Each statement is gated on its predecessor succeeding, so
// execution stops at the first failure (matching the Rust and
// Python drivers and the `sequence` request).
steps = userSteps.map((step, i) =>
i === 0 ? step : { ...step, condition: { type: 'ok' as const, step: i - 1 } },
);
} else {
// Atomic batch: BEGIN <mode>, then each user step gated on itsView on GitHub (pinned to c1e5928725)
Solutions
- Strip transaction-control statements; pass the batch mode and let the library manage BEGIN/COMMIT/ROLLBACK.
- If manual control is required, omit the mode argument so no atomic wrap is added, or send those statements via execute().
- Pre-process SQL scripts to split on statement and filter out keywords before batching.
Example fix
// before await session.batch(["BEGIN", "UPDATE t SET x=1", "COMMIT"], "write"); // after await session.batch(["UPDATE t SET x=1"], "write");
Defensive patterns
Strategy: validation
Validate before calling
const TXN = new Set(["BEGIN","COMMIT","END","ROLLBACK","SAVEPOINT","RELEASE"]);
const rejectTxnInBatch = (statements) => statements.forEach((s, i) => {
const sql = typeof s === 'string' ? s : s.sql;
const kw = sql.trim().split(/\s+/)[0]?.toUpperCase();
if (TXN.has(kw)) throw new Error(`statement ${i}: ${kw} not allowed in atomic batch`);
}); Type guard
const isTxnControl = (sql: string): boolean => TXN.has(sql.trim().split(/\s+/)[0]?.toUpperCase() ?? "");
Try / catch
try {
await session.batch(statements, mode);
} catch (e) {
if (/not allowed in an atomic batch/.test(e?.message ?? "")) {
const filtered = statements.filter(s => !isTxnControl(typeof s === 'string' ? s : s.sql));
return session.batch(filtered, mode);
}
throw e;
} Prevention
- Pass a BatchMode instead of writing BEGIN/COMMIT yourself
- Strip transaction keywords from SQL dumps before batching
- Use execute() for statements that must manage their own transactions
- Add a pre-flight keyword check when building statements dynamically
When it happens
Trigger: Calling session.batch(statements, mode) where any statement string (or statement.sql) begins with a TRANSACTION_CONTROL_KEYWORDS entry — e.g. a SQL dump containing BEGIN;...COMMIT; passed as batch statements with mode 'write'.
Common situations: Importing .sql dump files via batch(); converting imperative execute('BEGIN') code to batch with a mode; template-generated SQL that includes transaction frames; applying a mode parameter where previously none was passed.
Related errors
- batch statement ${index} failed: ${keyword} is not allowed i
- batch statement ${index} failed: ${message}
- 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/44503c956087fec8.
Report an issue: GitHub.