{"record":{"id":"f48bac72dab542d8","repo":"tursodatabase/turso","slug":"batch-statement-index-failed-message","errorCode":null,"errorMessage":"batch statement ${index} failed: ${message}","messagePattern":"batch statement (.+?) failed: (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"bindings/javascript/packages/common/promise.ts","lineNumber":799,"sourceCode":"      exec.reset();\n    }\n  };\n\n  const { mode, raw } = normalizeBatchOptions(options);\n  const wrap = mode != null && !native.inTransaction();\n  const normalizedStatements = statements.map((statement, index): BatchStatement => {\n    if (typeof statement === \"string\" || statement.args === undefined) {\n      return statement;\n    }\n    try {\n      const args = Array.isArray(statement.args)\n        ? statement.args.map(normalizeBatchBindValue)\n        : Object.fromEntries(\n          Object.entries(statement.args).map(([name, value]) => [name, normalizeBatchBindValue(value)]),\n        );\n      return { sql: statement.sql, args };\n    } catch (error) {\n      throw batchInputError(index, error);\n    }\n  });\n  if (wrap) {\n    for (let index = 0; index < normalizedStatements.length; index++) {\n      const statement = normalizedStatements[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, new Error(`${keyword} is not allowed in an atomic batch`));\n      }\n    }\n  }\n  if (wrap) {\n    await runRawSql(`BEGIN ${normalizeBatchMode(mode!)}`);\n  }\n\n  const results: ResultSet[] = [];\n  const executeStatement = async (","sourceCodeStart":781,"sourceCodeEnd":817,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/bindings/javascript/packages/common/promise.ts#L781-L817","documentation":"During client.batch(), each statement's args are normalized (converts JS bind values to the wire format). If normalization throws for statement at zero-based index i, the library rethrows batchInputError(i, error) producing 'batch statement i failed: <message>' so the caller knows which statement contained the unencodable argument.","triggerScenarios":"Calling batch() with a statement whose args contain values that cannot be bound: unsupported JS types (undefined, objects, functions, BigInt out of range), invalid named-parameter containers, or normalizeBatchBindValue rejecting a value.","commonSituations":"Passing undefined from an unpopulated object field; passing a Date/class instance instead of a primitive; mixing named args object with array expectations; large BigInt values beyond i64 range.","solutions":["Read the index in the error message and inspect that statement's args for unsupported values.","Coerce values to supported bind types: number, string, bigint (within i64), ArrayBuffer/Uint8Array, or null.","Replace undefined with null explicitly before batching.","Use normalize/sanitize helpers on the args array/object before calling batch()."],"exampleFix":"// before\nawait db.batch([{ sql: \"INSERT INTO t VALUES (?)\", args: [user.createdAt] }]); // Date/undefined\n// after\nconst safe = (v) => v === undefined ? null : (v instanceof Date ? v.toISOString() : v);\nawait db.batch([{ sql: \"INSERT INTO t VALUES (?)\", args: [safe(user.createdAt)] }]);","handlingStrategy":"validation","validationCode":"function isBindable(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 || v instanceof ArrayBuffer;\n}\nstatements.forEach((s, i) => (Array.isArray(s.args) ? s.args : Object.values(s.args ?? {}))\n  .forEach(v => { if (!isBindable(v)) throw new Error(`bad arg in statement ${i}: ${v}`); }));","typeGuard":"const isBindable = (v: unknown): v is string | number | bigint | Uint8Array | ArrayBuffer | null =>\n  v === null || typeof v === 'number' || typeof v === 'string' ||\n  (typeof v === 'bigint' && v >= -(2n**63n) && v < 2n**63n) ||\n  v instanceof Uint8Array || v instanceof ArrayBuffer;","tryCatchPattern":"try {\n  const results = await db.batch(statements);\n} catch (e) {\n  if (typeof e.message === 'string' && /batch statement \\d+ failed/.test(e.message)) {\n    const idx = Number(e.message.match(/\\d+/)[0]);\n    console.error(`Statement ${idx} has invalid args`, statements[idx].args);\n  } else throw e;\n}","preventionTips":["Coerce optional fields with ?? null before batching","Convert Date and class instances to strings/JSON explicitly","Range-check BigInt values to signed 64-bit","Validate args against a whitelist of bindable types before calling batch()"],"tags":["javascript","typescript","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"}