{"record":{"id":"208fbdc70619c38b","repo":"tursodatabase/turso","slug":"transactionasync-callbacks-receive-a-transaction-208fbd","errorCode":null,"errorMessage":"transactionAsync() callbacks receive a Transaction handle as their first argument and must declare it: db.transactionAsync(async (tx, ...args) => { await tx.run(...) }).","messagePattern":"transactionAsync\\(\\) callbacks receive a Transaction handle as their first argument and must declare it: db\\.transactionAsync\\(async \\(tx, \\.\\.\\.args\\) => (.+?)\\)\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"serverless/javascript/src/connection.ts","lineNumber":505,"sourceCode":"   *\n   * @example\n   * ```typescript\n   * const insertMany = client.transactionAsync(async (tx, users) => {\n   *   const insert = await tx.prepare(\"INSERT INTO users (name) VALUES (?)\");\n   *   for (const user of users) {\n   *     await insert.run([user]);\n   *   }\n   * });\n   *\n   * await insertMany(['Alice', 'Bob', 'Charlie']);\n   * ```\n   */\n  transactionAsync(fn: (tx: Transaction, ...args: any[]) => any): any {\n    if (typeof fn !== \"function\") {\n      throw new TypeError(\"Expected first argument to be a function\");\n    }\n    if (fn.length === 0) {\n      throw new TypeError(\n        \"transactionAsync() callbacks receive a Transaction handle as their first argument \" +\n        \"and must declare it: db.transactionAsync(async (tx, ...args) => { await tx.run(...) }).\",\n      );\n    }\n\n    const db = this;\n    const wrapTxn = (mode: string) => {\n      return async (...bindParameters: any[]) => {\n        if (!db.isOpen) {\n          throw new TypeError(\"The database connection is not open\");\n        }\n        // The transaction owns a dedicated session (server stream), so the\n        // connection is not locked for its duration: concurrent statements\n        // on the connection run on its own stream, outside the transaction.\n        const session = new Session(db.config);\n        const txn = new Transaction(session, db.defaultSafeIntegerMode);\n        try {\n          await txn.exec(\"BEGIN \" + mode);","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/serverless/javascript/src/connection.ts#L487-L523","documentation":"`transactionAsync()` inspects the callback's declared arity (`fn.length`) and rejects zero-parameter callbacks. The first declared parameter receives the Transaction handle that owns a dedicated session; a callback that declares no parameters cannot use it, so it is refused rather than silently running statements on the wrong session. Note that rest-only arrows like `(...args) => {}` also report length 0.","triggerScenarios":"`db.transactionAsync(async () => { ... })`; `db.transactionAsync(async (...args) => { ... })` - both declare zero required leading parameters; a linter auto-removing the unused `tx` parameter.","commonSituations":"Converting synchronous transaction() code whose callback took no handle; strict unused-parameter lint rules deleting the tx binding; copy-paste between the sync and async transaction APIs.","solutions":["Declare the handle first: db.transactionAsync(async (tx, ...args) => { await tx.run(...) })","Configure your linter to ignore unused function parameters (argsIgnorePattern) so `tx` survives"],"exampleFix":"// before\nawait db.transactionAsync(async () => {\n  await db.run('INSERT INTO t VALUES (1)');\n})();\n// after\nawait db.transactionAsync(async (tx) => {\n  await tx.run('INSERT INTO t VALUES (1)');\n})();","handlingStrategy":"validation","validationCode":"if (typeof fn !== 'function' || fn.length === 0) {\n  throw new Error('transactionAsync callback must declare a tx parameter: async (tx, ...args) => {}');\n}\nawait db.transactionAsync(fn).immediate();","typeGuard":"const declaresTxParam = (fn: unknown): fn is (tx: Transaction, ...args: any[]) => any =>\n  typeof fn === 'function' && fn.length > 0;","tryCatchPattern":null,"preventionTips":["Always write transactionAsync callbacks as async (tx, ...args) => {}","Configure ESLint's unused-args rule (argsIgnorePattern: '^_') so the tx parameter is never auto-deleted"],"tags":["typescript","callback","transaction","api-misuse","linting"],"backgroundTag":"missing-callback-parameter","analyzedSha":"c1e59287258d99b309e362a63f48822256e2f65f","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}