tursodatabase/turso · error · TypeError
Expected first argument to be a function
Error message
Expected first argument to be a function
What it means
transaction(fn) wraps the given callback so that the returned function executes it inside a BEGIN..COMMIT window, and it validates up front that fn is callable. Passing anything that is not a function — the result of invoking the callback early, a Promise, or undefined — fails fast with this TypeError before any transaction is started.
Solutions
- Pass the function reference itself: db.transaction(fn), then invoke the returned wrapper
- Remove any early invocation — the () after a callback expression turns it into a value
- When fn comes from dynamic input, guard with typeof fn === "function" before calling transaction()
Example fix
// before const run = db.transaction(processOrder(order)); // calls now, passes a Promise // after const run = db.transaction(() => processOrder(order)); // pass a function await run();
Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof fn !== "function") {
throw new TypeError("transaction() needs the callback function itself");
}
const run = db.transaction(fn); Type guard
function isCallable(fn: unknown): fn is (...args: unknown[]) => unknown {
return typeof fn === "function";
} Try / catch
try {
await db.transaction(fn)();
} catch (err) {
if (err instanceof TypeError && /first argument/i.test(err.message)) {
// fn was not a function: fix the call site, do not retry
throw new Error("transaction() requires a function argument");
}
throw err;
} Prevention
- Pass the function reference, never its invocation result
- Type the parameter explicitly (fn: () => Promise<T>) so TypeScript rejects non-functions at compile time
- Avoid any-typed callback sources; validate dynamic input with typeof before calling transaction()
When it happens
Trigger: db.transaction(runJob()) invokes runJob immediately and passes its return value; db.transaction(undefined) when the callback variable is misnamed, conditionally assigned, or lost in a refactor; passing a promise from an async IIFE instead of the function itself. Same behavior in the turbopack variant.
Common situations: Refactoring call sites around the wrapped-function API; dynamically selecting a callback that can be undefined; any-typed configuration that lets a non-function slip past TypeScript and fail at runtime.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Expected first argument to be a function
- Expected first argument to be a function
- Expected first argument to be a function
- transactionAsync() callbacks receive a Transaction handle…
- batch statement failed: is not allowed in an atomic batch
AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-08-20).
Data as JSON: /api/errors/7c9612f1fa817cb9.
Report an issue: GitHub.
Appendix: source
Thrown at bindings/javascript/sync/packages/wasm/promise-turbopack-hack.ts:277
const isReadonly = category === "read";
return new RemoteWriteStatement(
localStmt,
sql,
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.
*/
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 6c72522679)