tursodatabase/turso · error · Error

Unknown parameter name: ${name}

Error message

Unknown parameter name: ${name}

What it means

When bind() receives a single object, bindNamed() iterates its entries and asks the native statement for each parameter's index via namedPosition(). SQLite named parameters carry a :, @, or $ prefix; if the SQL contains no parameter matching the given name (typo, extra key, or prefix-form mismatch), namedPosition returns a negative value and the binding throws 'Unknown parameter name'. The whole bind is rejected before any value is bound.

Source

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

    for (let i = 0; i < params.length; i++) {
      const position = i + 1; // 1-indexed
      const value = params[i]!;

      this.bindValue(position, value);
    }
  }

  /**
   * Bind named parameters
   *
   * @param params - Object with named parameters
   */
  private bindNamed(params: Record<string, SQLiteValue>): void {
    for (const [name, value] of Object.entries(params)) {
      // Get position for named parameter
      const position = this._statement.namedPosition(name);
      if (position < 0) {
        throw new Error(`Unknown parameter name: ${name}`);
      }

      this.bindValue(position, value);
    }
  }

  /**
   * Bind a single value at a position
   *
   * @param position - 1-indexed position
   * @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)) {

View on GitHub (pinned to bad083fafb)

Solutions

  1. Make every object key match a named parameter in the SQL text exactly (watch the :, @, $ prefix form on both sides)
  2. Strip extraneous keys before binding — bind only the parameters the statement actually declares
  3. Centralize parameter names as constants shared by the SQL template and the bind object so they cannot drift

Example fix

// before
const stmt = conn.prepare('SELECT * FROM users WHERE id = :userId');
stmt.bind({ id: 42 }); // throws: Unknown parameter name: id

// after
const stmt = conn.prepare('SELECT * FROM users WHERE id = :userId');
stmt.bind({ userId: 42 });
Defensive patterns

Strategy: validation

Validate before calling

// Validate keys against the SQL's own parameter names before binding
function namedParamsOf(sql: string): Set<string> {
  return new Set(
    (sql.match(/[:@$][A-Za-z_][\w$]*/g) ?? []).map((p) => p.replace(/^[:@$]/, ''))
  );
}

function pickBindObject(sql: string, obj: Record<string, SQLiteValue>) {
  const known = namedParamsOf(sql);
  const out: Record<string, SQLiteValue> = {};
  for (const [k, v] of Object.entries(obj)) {
    if (known.has(k.replace(/^[:@$]/, ''))) out[k] = v;
  }
  return out; // extraneous keys dropped, typos become 'missing param' which is easier to spot
}

Try / catch

try { stmt.bind(params); } catch (e) { if (e instanceof Error && /Unknown parameter name/.test(e.message)) { throw new Error(`${e.message} — SQL expects one of: ${[...namedParamsOf(sql)]}`); } throw e; }

Prevention

When it happens

Trigger: `stmt.bind({ id: 1 })` where the SQL is 'SELECT * FROM t WHERE id = :userId' (id vs userId); passing an options object with extraneous keys alongside the real parameters; SQL rewritten to rename or remove a parameter while caller code still passes the old key; prefix conventions mixed (':name' in SQL but '@name' passed, where matching is prefix-sensitive).

Common situations: Passing a row object plus metadata keys (e.g., reusing a request body as bind params); renaming SQL parameters during a refactor; building dynamic WHERE clauses where the parameter set and the object keys drift; copy-pasting SQL from one query to another with different parameter names.

Related errors


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