tursodatabase/turso · error · TypeError
Expected first argument to be a function
Error message
Expected first argument to be a function
What it means
transaction() builds a wrapper that runs your callback between BEGIN and COMMIT, so the first argument must be a function; anything else throws this TypeError immediately. Note the source marks this wrapper deprecated precisely because it does not own the connection — concurrent statements can interleave into the transaction window — but the argument check applies regardless. The thrown error names the exact expectation.
Source
Thrown at bindings/javascript/sync/packages/native/promise.ts:267
isReadonly,
this.#remoteWriter,
() => this.pull(),
) as any;
}
/**
* Returns a function that executes the given function in a transaction.
* When remoteWrites is enabled, the entire transaction goes to remote.
*
* @deprecated Use {@link transactionAsync} instead: this wrapper does
* not own the connection, so concurrent statements can interleave into
* the transaction's window.
*/
override transaction<F extends (...args: any[]) => Promise<any>>(
fn: F,
): TransactionFunction<F> {
if (typeof fn !== "function")
throw new TypeError("Expected first argument to be a function");
if (!this.#remoteWriter) {
return super.transaction(fn);
}
const db = this;
const remoteWriter = this.#remoteWriter;
const wrapTxn = (mode: string) => {
return async (...bindParameters: any[]) => {
await remoteWriter.beginTransaction(mode);
try {
const result = await fn(...bindParameters);
await remoteWriter.commitTransaction();
await db.pull();
return result;
} catch (err) {
await remoteWriter.rollbackTransaction();
throw err;View on GitHub (pinned to bad083fafb)
Solutions
- Pass a function: db.transaction(async () => { ... }) and call the returned wrapper.
- Check typeof fn === 'function' at the call boundary in JavaScript code.
- Rely on TypeScript's generic constraint F extends (...args: any[]) => Promise<any> to catch this at compile time.
Example fix
// before
const runTxn = db.transaction(userCallbackOrSql); // may be a string/undefined
// after
if (typeof userCallbackOrSql !== 'function') {
throw new TypeError('transaction() requires a callback function');
}
const runTxn = db.transaction(userCallbackOrSql); Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof fn !== 'function') {
throw new TypeError('transaction() requires a callback function');
}
const runTxn = db.transaction(fn); Type guard
const isTxnCallback = (f: unknown): f is (...args: any[]) => Promise<any> => typeof f === 'function';
Prevention
- Rely on TypeScript so the generic constraint catches non-functions at compile time.
- Never pass raw SQL strings — this API takes a callback, unlike some other drivers.
- Check method references are bound before handing them to transaction().
When it happens
Trigger: db.transaction('SELECT ...') passing SQL text instead of a callback (pattern from other drivers); db.transaction() with undefined because the callback variable was misspelled or hoisted incorrectly; passing an options object or an array of statements.
Common situations: Migrating from sqlite3/better-sqlite3 APIs or raw SQL-string transaction helpers; passing a method reference that lost its binding and became undefined; refactors renaming the callback parameter.
Related errors
- No active remote transaction
- transactionAsync is not supported with remoteWritesExperimen
- Expected first argument to be a function
- transactionAsync() callbacks receive a Transaction handle as
- The transaction has already completed
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/9ec376235e7abe62.
Report an issue: GitHub.