tursodatabase/turso · error · TypeError

Expected first argument to be an array of statements

Error message

Expected first argument to be an array of statements

What it means

Thrown by Database.batch() when the first argument is not an array. batch() expects an array of statements, each either a SQL string or an object { sql, args }, so passing a single statement, a variadic list, or a non-array (string, object, undefined) is rejected with this TypeError before any connection state is touched.

Source

Thrown at bindings/javascript/packages/common/compat.ts:293

        if (stepResult === STEP_DONE) {
          break;
        }
        if (stepResult === STEP_ROW) {
          // For exec(), we don't need the row data, just continue
          continue;
        }
      }
    } finally {
      exec.reset();
    }
  }

  batch(
    statements: Array<string | { sql: string; args?: any[] | Record<string, any> }>,
    options?: BatchMode | BatchOptions,
  ): ResultSet[] {
    if (!Array.isArray(statements)) {
      throw new TypeError("Expected first argument to be an array of statements");
    }
    if (!this.open) {
      throw new TypeError("The database connection is not open");
    }

    const { mode, raw } = normalizeBatchOptions(options);
    const wrap = mode != null && !this.db.inTransaction();
    if (wrap) {
      this.exec(`BEGIN ${normalizeBatchMode(mode!)}`);
    }

    const results: ResultSet[] = [];
    try {
      for (const statement of statements) {
        const sql = typeof statement === "string" ? statement : statement.sql;
        const args = typeof statement === "string" ? undefined : statement.args;
        const stmt = this.db.prepare(sql);
        try {

View on GitHub (pinned to bad083fafb)

Solutions

  1. Wrap statements in an array: db.batch(['INSERT ...', { sql: 'UPDATE ...', args: [id] }])
  2. Convert iterables explicitly: db.batch(Array.from(map.values()))
  3. Guard dynamic statement lists: if (!Array.isArray(stmts) || stmts.length === 0) skip
  4. Use a single prepare().run() when you only have one statement - batch is for groups

Example fix

// before
db.batch('DELETE FROM a; DELETE FROM b'); // single string -> TypeError

// after
db.batch(['DELETE FROM a', 'DELETE FROM b']);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!Array.isArray(statements)) {
  statements = [statements]; // or throw with context
}
if (statements.length === 0) return [];
db.batch(statements);

Type guard

type BatchStatement = string | { sql: string; args?: any[] | Record<string, any> };
function isBatchInput(v: unknown): v is BatchStatement[] {
  return Array.isArray(v) && v.every((s) => typeof s === 'string' || (s != null && typeof s === 'object' && typeof s.sql === 'string'));
}

Prevention

When it happens

Trigger: Calling db.batch('INSERT INTO t VALUES (1)') with a bare string instead of an array; calling db.batch(stmt1, stmt2) expecting variadic behavior; passing a generator or Set of statements; passing undefined because a build step produced no statements.

Common situations: Migrating from an API that accepts a single statement; building statements conditionally and forgetting to wrap them in an array; converting from a Map/generator without Array.from; off-by-one refactors where the array literal is dropped.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/c5385315969dded9. Report an issue: GitHub.