{"record":{"id":"441c02edd7a44946","repo":"tursodatabase/turso","slug":"expected-first-argument-to-be-a-function-441c02","errorCode":null,"errorMessage":"Expected first argument to be a function","messagePattern":"Expected first argument to be a function","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"serverless/javascript/src/connection.ts","lineNumber":395,"sourceCode":"   *\n   * @param fn - The function to wrap in a transaction\n   * @returns A function that will execute fn within a transaction\n   *\n   * @example\n   * ```typescript\n   * const insert = await client.prepare(\"INSERT INTO users (name) VALUES (?)\");\n   * const insertMany = client.transaction((users) => {\n   *   for (const user of users) {\n   *     insert.run([user]);\n   *   }\n   * });\n   *\n   * await insertMany(['Alice', 'Bob', 'Charlie']);\n   * ```\n   */\n  transaction(fn: (...args: any[]) => any): any {\n    if (typeof fn !== \"function\") {\n      throw new TypeError(\"Expected first argument to be a function\");\n    }\n\n    const db = this;\n    const wrapTxn = (mode: string) => {\n      return async (...bindParameters: any[]) => {\n        await db.exec(\"BEGIN \" + mode);\n        try {\n          const result = await fn(...bindParameters);\n          await db.exec(\"COMMIT\");\n          return result;\n        } catch (err) {\n          await db.exec(\"ROLLBACK\");\n          throw err;\n        }\n      };\n    };\n\n    const properties = {","sourceCodeStart":377,"sourceCodeEnd":413,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/serverless/javascript/src/connection.ts#L377-L413","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst insertMany = client.transaction(); // no fn passed\n// later: insertMany([...]) — or client.transaction(someObject)\n\n// after\nconst insertMany = client.transaction((users) => {\n  for (const user of users) insert.run([user]);\n});\nawait insertMany(['Alice', 'Bob']);","handlingStrategy":"type-guard","validationCode":"if (typeof fn !== 'function') {\n  throw new Error('transaction() requires a callback function');\n}\nconst wrapped = db.transaction(fn);","typeGuard":"function isCallable<T extends (...args: any[]) => any>(v: T | unknown): v is T {\n  return typeof v === 'function';\n}","tryCatchPattern":"try {\n  const wrapped = db.transaction(fn);\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes('Expected first argument to be a function')) {\n    throw new TypeError('pass a function reference, not its result, to transaction()');\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["validation","typeerror","transaction","api-misuse"],"backgroundTag":"invalid-argument-type","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}