{"record":{"id":"69447e514cbe163c","repo":"tursodatabase/turso","slug":"batch-statement-index-failed-message-69447e","errorCode":null,"errorMessage":"batch statement ${index} failed: ${message}","messagePattern":"batch statement (.+?) failed: (.+?)","errorType":"exception","errorClass":"DatabaseError","httpStatus":null,"severity":"error","filePath":"serverless/javascript/src/session.ts","lineNumber":521,"sourceCode":"    mode?: BatchMode,\n    queryOptions?: QueryOptions,\n    safeIntegers: boolean = false,\n    raw: boolean = false,\n  ): Promise<any> {\n    const userSteps: BatchStep[] = statements.map((statement, index) => {\n      if (typeof statement === 'string') {\n        return {\n          stmt: { sql: statement, args: [], named_args: [], want_rows: true },\n        };\n      }\n      // A value that cannot be encoded fails client-side before anything\n      // is sent; report it with the statement's index like any other\n      // statement failure. Nothing has executed, so batchResults is empty.\n      let encodedArgs;\n      try {\n        encodedArgs = encodeSqlArgs(statement.args ?? []);\n      } catch (e: any) {\n        throw batchInputError(index, e?.message ?? String(e));\n      }\n      return {\n        stmt: {\n          sql: statement.sql,\n          args: encodedArgs.args,\n          named_args: encodedArgs.namedArgs,\n          want_rows: true,\n        },\n      };\n    });\n\n    if (mode !== undefined) {\n      for (let index = 0; index < statements.length; index++) {\n        const statement = statements[index];\n        const sql = typeof statement === 'string' ? statement : statement.sql;\n        const keyword = firstSqlKeyword(sql);\n        if (keyword !== undefined && TRANSACTION_CONTROL_KEYWORDS.has(keyword)) {\n          throw batchInputError(index, `${keyword} is not allowed in an atomic batch`);","sourceCodeStart":503,"sourceCodeEnd":539,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/serverless/javascript/src/session.ts#L503-L539","documentation":"In the serverless JS client's session.batch(), each statement's args are encoded client-side via encodeSqlArgs before anything is sent. If encoding throws (unsupported value types), the error is rethrown as batchInputError(index, message) with the statement's zero-based index; since nothing has executed, batchResults in the resulting DatabaseError is empty.","triggerScenarios":"Calling session.batch() with a statement whose args array or named-args record contains values encodeSqlArgs cannot serialize: undefined, unsupported objects, invalid BigInt, wrong container type.","commonSituations":"Optional JS fields left undefined; sending class instances/Dates instead of primitives; passing null-like placeholder values the encoder rejects; refactored code where args switched between array and named-object form.","solutions":["Use the batchIndex on the thrown DatabaseError to find the offending statement and fix its args.","Convert values to supported types: null, number, string, bigint (i64 range), or Uint8Array.","Replace undefined with explicit null; serialize Dates/objects to strings/JSON before batching.","Encode args once yourself with the same encoder to validate before calling batch()."],"exampleFix":"// before\nawait session.batch([{ sql: \"INSERT INTO t VALUES (?)\", args: [meta.tags] }]); // undefined\n// after\nawait session.batch([{ sql: \"INSERT INTO t VALUES (?)\", args: [meta.tags ?? null] }]);","handlingStrategy":"validation","validationCode":"function isEncodable(v) {\n  return v === null || typeof v === 'number' || typeof v === 'string' ||\n    (typeof v === 'bigint' && v >= -(2n**63n) && v < 2n**63n) ||\n    v instanceof Uint8Array;\n}\nstatements.forEach((s, i) => (s.args ?? []).forEach(v => {\n  if (!isEncodable(v)) throw new Error(`statement ${i}: unencodable arg ${v}`);\n}));","typeGuard":"const isEncodable = (v: unknown): v is null | string | number | bigint | Uint8Array =>\n  v === null || typeof v === 'number' || typeof v === 'string' ||\n  (typeof v === 'bigint' && v >= -(2n**63n) && v < 2n**63n) ||\n  v instanceof Uint8Array;","tryCatchPattern":"try {\n  await session.batch(statements, mode);\n} catch (e) {\n  if (e?.batchIndex !== undefined) {\n    console.error(`statement ${e.batchIndex} failed`, e.message, e.batchResults);\n  } else throw e;\n}","preventionTips":["Replace undefined args with explicit null before batching","Serialize Dates/objects to strings/JSON at the boundary","Keep args as arrays or named records matching the encoder's expectations","Validate BigInt range and binary types before batch()"],"tags":["javascript","serverless","batch","parameter-binding"],"backgroundTag":"batch-statement-failed","analyzedSha":"c1e59287258d99b309e362a63f48822256e2f65f","analyzedAt":"2026-08-31T11:17:35.598Z","contentChangedAt":"2026-08-31T11:17:35.598Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}