tursodatabase/turso · error · TypeError

Only finite numbers (not Infinity or NaN) can be passed as a

Error message

Only finite numbers (not Infinity or NaN) can be passed as arguments

What it means

The JavaScript driver converts bind values into SQLite types before executing statements. SQLite numbers are 64-bit IEEE floats or signed 64-bit integers, so Infinity and NaN have no representation. normalizeBatchBindValue rejects them eagerly with a TypeError instead of silently corrupting the stored value.

Source

Thrown at bindings/javascript/packages/common/promise.ts:916

    }
  } catch (err) {
    if (wrap) {
      try {
        await runRawSql("ROLLBACK");
      } catch (rollbackError) {
        throw attachRollbackError(err, rollbackError);
      }
    }
    throw err;
  }
  return results;
}

function normalizeBatchBindValue(value: any): any {
  if (value === null || value === undefined) return null;
  if (typeof value === "number") {
    if (!Number.isFinite(value)) {
      throw new TypeError("Only finite numbers (not Infinity or NaN) can be passed as arguments");
    }
    return value;
  }
  if (typeof value === "bigint") {
    if (value < -(1n << 63n) || value > (1n << 63n) - 1n) {
      throw new TypeError("BigInt value is outside SQLite's signed 64-bit integer range");
    }
    return value;
  }
  if (
    typeof value === "boolean" ||
    typeof value === "string" ||
    (ArrayBuffer.isView(value) && !(value instanceof DataView))
  ) {
    return value;
  }
  if (value instanceof ArrayBuffer) return new Uint8Array(value);
  return String(value);

View on GitHub (pinned to c1e5928725)

Solutions

  1. Fix the caller code so the bound value is finite: handle or clamp Infinity/NaN before the batch call.
  2. Bind null instead when the value is not finite.
  3. If a large sentinel was intended, bind a finite literal or a string instead of Infinity.
  4. Store as TEXT if you genuinely need to persist Infinity-like values.

Example fix

// before
const ratio = total === 0 ? Infinity : total / count;
await db.batch([stmt.bind(ratio)]);
// after
const ratio = total === 0 ? null : total / count;
await db.batch([stmt.bind(ratio)]);
Defensive patterns

Strategy: validation

Validate before calling

function isFiniteBind(v) { return v === null || v === undefined || (typeof v === 'number' && Number.isFinite(v)) || typeof v === 'bigint' || typeof v === 'boolean' || typeof v === 'string' || (v instanceof Date && Number.isFinite(v.getTime())) || v instanceof Uint8Array; }
if (!args.every(isFiniteBind)) throw new TypeError('bind args must be finite');

Type guard

const isBindableNumber = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);

Try / catch

try { await db.batch(stmts); } catch (e) { if (e instanceof TypeError && /finite numbers/.test(e.message)) { /* sanitize args and retry with nulls */ } else throw e; }

Prevention

When it happens

Trigger: Calling db.batch() (or any batch path that runs normalizeBatchBindValue) with a parameter that is Infinity, -Infinity, or NaN, e.g. binding 1/0 or NaN computed earlier in the pipeline.

Common situations: Dividing by zero before binding, Math.log(-1), JSON parsing of 'Infinity' (JSON.stringify turns it into null but arithmetic on sparse data yields NaN), passing an unset accumulator variable initialized to Infinity.

Related errors


AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-31). Data as JSON: /api/errors/752beda2742b10c6. Report an issue: GitHub.