tursodatabase/turso · error · Error

Unsupported parameter type

Error message

Unsupported parameter type: ${typeof value}

What it means

bindValue() accepts numbers (int via bindPositionalInt, floats via bindPositionalDouble), strings (bindPositionalText), and blob data (ArrayBuffer or typed-array views via bindPositionalBlob). Anything else — null/undefined, booleans, plain objects, arrays, Dates — falls through to this TypeError describing the offending typeof.

Solutions

  1. Map values before binding: null/undefined -> sql null support via explicit conversion your schema expects (e.g. bind 0/'' or restructure to avoid the slot), booleans -> 0/1, Dates -> ISO strings, objects/arrays -> JSON.stringify
  2. Add a normalize(value) helper at your data layer so every bound value is number|string|ArrayBuffer
  3. Type your params as SQLiteValue (number | string | ArrayBuffer | TypedArray) so invalid types fail at compile time

Example fix

// before
await stmt.run({
  id: 1,
  active: true,          // typeof 'boolean'
  created: new Date(),   // typeof 'object'
});

// after
await stmt.run({
  id: 1,
  active: 1,
  created: new Date().toISOString(),
});
Defensive patterns

Strategy: validation

Validate before calling

type SqlValue = number | string | ArrayBuffer;

function normalize(v: unknown): SqlValue {
  if (v == null) return '';            // or model NULL explicitly per your schema
  if (typeof v === 'boolean') return v ? 1 : 0;
  if (v instanceof Date) return v.toISOString();
  if (typeof v === 'object') return JSON.stringify(v);
  if (typeof v === 'number' || typeof v === 'string') return v;
  throw new Error(`Cannot bind value of type ${typeof v}`);
}

const safeParams = rawParams.map(normalize);
await stmt.run(...safeParams);

Type guard

function isBindable(v: unknown): v is number | string | ArrayBuffer | ArrayBufferView {
  return (
    typeof v === 'number' ||
    typeof v === 'string' ||
    v instanceof ArrayBuffer ||
    ArrayBuffer.isView(v)
  );
}

Try / catch

try {
  await stmt.run(params);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Unsupported parameter type:')) {
    throw new TypeError(`Bound value not a SQLite type: got ${e.message.split(': ')[1]}. Convert booleans/Dates/objects first.`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Binding null or undefined (e.g. optional field that is unset), a boolean flag, a Date object, a plain object/array expecting JSON serialization, or a BigInt — all reach the final else branch of bindValue.

Common situations: Inserting API payloads where fields can be null; passing `true/false` for a column stored as 0/1; forgetting to call date.toISOString(); assuming JSON columns auto-serialize; BigInt keys from other libraries.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/e1bc9d57de3d3042. Report an issue: GitHub.

Appendix: source

Thrown at bindings/react-native/src/Statement.ts:118

   * @param value - Value to bind
   */
  private bindValue(position: number, value: SQLiteValue): void {
    if (value === null || value === undefined) {
      this._statement.bindPositionalNull(position);
    } else if (typeof value === 'number') {
      // Check if integer or float
      if (Number.isInteger(value)) {
        this._statement.bindPositionalInt(position, value);
      } else {
        this._statement.bindPositionalDouble(position, value);
      }
    } else if (typeof value === 'string') {
      this._statement.bindPositionalText(position, value);
    } else if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
      const buffer = value as unknown as ArrayBuffer;
      this._statement.bindPositionalBlob(position, buffer);
    } else {
      throw new Error(`Unsupported parameter type: ${typeof value}`);
    }
  }

  /**
   * Execute statement without returning rows (for INSERT, UPDATE, DELETE)
   *
   * @param params - Optional parameters to bind
   * @returns Result with changes and lastInsertRowid
   */
  async run(...params: BindParams[]): Promise<RunResult> {
    if (this._finalized) {
      throw new Error('Statement has been finalized');
    }

    if (this._execLock) {
      await this._execLock.acquire();
    }
    try {

View on GitHub (pinned to 244cde92a7)