tursodatabase/turso · error · Error

Failed to get column name at index ${i}

Error message

Failed to get column name at index ${i}

What it means

Statement.readRow() iterates from 0 to columnCount()-1 calling the native columnName(i); a falsy (null or empty) name at any index throws with that index. The count and names come from the native JSI host object, so for a valid prepared SELECT this should be impossible — hitting it means the JSI metadata layer is not returning what the JS side expects.

Source

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

      if (this._execLock) {
        this._execLock.release();
      }
    }
  }

  /**
   * Read current row into an object
   *
   * @returns Row object with column name keys
   */
  private readRow(): Row {
    const row: Row = {};
    const columnCount = this._statement.columnCount();

    for (let i = 0; i < columnCount; i++) {
      const name = this._statement.columnName(i);
      if (!name) {
        throw new Error(`Failed to get column name at index ${i}`);
      }

      const value = this.readColumnValue(i);
      row[name] = value;
    }

    return row;
  }

  /**
   * Read value at column index
   *
   * @param index - Column index
   * @returns Column value
   */
  private readColumnValue(index: number): SQLiteValue {
    const kind = this._statement.rowValueKind(index);

View on GitHub (pinned to bad083fafb)

Solutions

  1. Fully rebuild the native module: cd ios && pod install (or pod deintegrate && pod install) and a clean Android build (./gradlew clean)
  2. Reset Metro caches: npx react-native start --reset-cache
  3. Pin the npm package and the native pod/package to matching versions
  4. Ensure no code path finalizes the statement or closes the database while a get() row read is in flight

Example fix

# before
npm install @tursodatabase/sync-react-native@latest
# app throws: Failed to get column name at index 3

# after
cd ios && pod deintegrate && pod install && cd ..
cd android && ./gradlew clean && cd ..
npx react-native start --reset-cache
Defensive patterns

Strategy: try-catch

Type guard

function isColumnNameError(e: unknown): boolean {
  return e instanceof Error && /Failed to get column name at index \d+/.test(e.message);
}

Try / catch

try {
  const row = await stmt.get(id);
} catch (e) {
  if (isColumnNameError(e)) {
    // binding/native skew: rebuild native + reset Metro, then retry once
    throw new Error('Native/JS binding mismatch — run pod install / gradlew clean and --reset-cache');
  }
  throw e;
}

Prevention

When it happens

Trigger: JS package and native binary built from different revisions (columnName returning undefined for an index the JS columnCount trusts); the statement being finalized concurrently between the columnCount() call and the name-read loop; a native build where the host object was not registered correctly.

Common situations: Upgrading @tursodatabase/sync-react-native in package.json without rebuilding pods/gradle artifacts; stale Metro cache serving old JS against a new native module; CI builds with cached native artifacts; Expo prebuild caches.

Related errors


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