tursodatabase/turso · error · TypeError
Expected first argument to be a function
Error message
Expected first argument to be a function
What it means
Connection.transaction(fn) builds synchronous-style transaction wrappers (like better-sqlite3's transaction()) and requires fn to be a function; anything else throws a plain TypeError immediately. The wrapper later drives fn between BEGIN and COMMIT/ROLLBACK via db.exec, so a non-function input cannot work at all.
Source
Thrown at serverless/javascript/src/connection.ts:395
*
* @param fn - The function to wrap in a transaction
* @returns A function that will execute fn within a transaction
*
* @example
* ```typescript
* const insert = await client.prepare("INSERT INTO users (name) VALUES (?)");
* const insertMany = client.transaction((users) => {
* for (const user of users) {
* insert.run([user]);
* }
* });
*
* await insertMany(['Alice', 'Bob', 'Charlie']);
* ```
*/
transaction(fn: (...args: any[]) => any): any {
if (typeof fn !== "function") {
throw new TypeError("Expected first argument to be a function");
}
const db = this;
const wrapTxn = (mode: string) => {
return async (...bindParameters: any[]) => {
await db.exec("BEGIN " + mode);
try {
const result = await fn(...bindParameters);
await db.exec("COMMIT");
return result;
} catch (err) {
await db.exec("ROLLBACK");
throw err;
}
};
};
const properties = {View on GitHub (pinned to bad083fafb)
Solutions
- Pass a function reference: db.transaction((...args) => { ... })
- If fn comes from an option, default it or validate it before calling transaction()
- Check typeof fn === 'function' in your own wrapper before delegating
- Make sure you are not invoking the helper and passing its return value
Example fix
// before
const insertMany = client.transaction(); // no fn passed
// later: insertMany([...]) — or client.transaction(someObject)
// after
const insertMany = client.transaction((users) => {
for (const user of users) insert.run([user]);
});
await insertMany(['Alice', 'Bob']); Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof fn !== 'function') {
throw new Error('transaction() requires a callback function');
}
const wrapped = db.transaction(fn); Type guard
function isCallable<T extends (...args: any[]) => any>(v: T | unknown): v is T {
return typeof v === 'function';
} Try / catch
try {
const wrapped = db.transaction(fn);
} catch (e) {
if (e instanceof TypeError && e.message.includes('Expected first argument to be a function')) {
throw new TypeError('pass a function reference, not its result, to transaction()');
}
throw e;
} Prevention
- Always pass arrow functions or named function references to transaction()
- Double-check for accidental invocation (trailing parentheses) when refactoring
- Make optional callbacks throw a clear error when absent instead of forwarding undefined
- Let TypeScript's signature (fn: (...args: any[]) => any) do the checking — avoid any-typed variables
When it happens
Trigger: Calling db.transaction(db.insertMany) incorrectly bound, passing the result of a call instead of the function (db.transaction(makeFn())), passing undefined because an optional helper was not provided, or passing an object with a run method.
Common situations: Refactoring from inline arrow functions to named helpers and losing the reference; optional-callback APIs defaulting to undefined; copy-paste from code that stored the returned function but calling transaction on the wrong variable.
Related errors
- Expected first argument to be an array of statements
- Expected first argument to be an array of statements
- Expected first argument to be a function
- Expected first argument to be a function
- Expected first argument to be a function
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/441c02edd7a44946.
Report an issue: GitHub.