tursodatabase/turso · error · Error

getAllRows: exceeded

Error message

getAllRows: exceeded ${MAX_IO_RETRIES} IO retries

What it means

Statement.all() on a sync/partial-sync database drives the native getAllRows() in a loop: each time the engine returns TursoStatus.IO it runs the pending IO (fetching missing pages over the network) and retries, capped at MAX_IO_RETRIES = 1000000. This error means the engine requested IO a million times without ever reaching DONE, i.e. the query made no forward progress and the loop was aborted instead of hanging forever.

Solutions

  1. Check that the device can reach the sync url (auth token valid, network up) - run setup({logLevel: 'debug'}) and inspect the HTTP logs emitted by ioProcessor
  2. Make the JS package and the native binary come from the same release: after upgrading @tursodatabase/sync-react-native run pod install (iOS) and a clean gradle build (Android)
  3. Close and re-create the Database connection, then retry the query once - a wedged sync-engine state usually clears on reconnect
  4. If it reproduces with matched versions and a reachable remote, capture sync logs and file an issue with the query and schema

Example fix

// before - stuck IO loop eventually throws
const rows = await db.all('SELECT * FROM big_table');

// after - bound the wait yourself and reconnect on the guard error
try {
  const rows = await db.all('SELECT * FROM big_table');
} catch (e) {
  if (e instanceof Error && e.message.startsWith('getAllRows: exceeded')) {
    await db.close();
    db = await connect(opts); // fresh connection, retry once
    return db.all('SELECT * FROM big_table');
  }
  throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

// Before a heavy query on a sync replica, confirm the remote is reachable so the IO loop can actually make progress
export async function assertSyncReachable(db: Database): Promise<void> {
  await db.sync(); // cheap round-trip; fails fast with the underlying network error instead of looping
}

Try / catch

catch (e) { if (e instanceof Error && e.message.startsWith('getAllRows: exceeded')) { await db.close(); db = await connect(opts); return db.all(sql); } throw e; }

Prevention

When it happens

Trigger: Calling stmt.all() / db.all() on a database opened with a sync url where every getAllRows() call returns IO again: the _extraIo drain (or runIo) is not satisfying the engine's request, e.g. HTTP page fetches that keep failing without poisoning the operation, or a native binary and JS package from different releases whose IO protocol never converges.

Common situations: Sync replica whose remote is unreachable or returning bad frames while the sync engine keeps re-requesting the same pages; upgrading only the npm package without rebuilding the native side (pod install / gradle); effectively an infinite IO loop that this guard converts into an error.

Related errors


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

Appendix: source

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

          rows = rows.concat(bulk.rows);
        }

        if (bulk.status === TursoStatus.DONE) {
          return rows;
        }

        if (bulk.status === TursoStatus.IO) {
          this._statement.runIo();
          if (this._extraIo) {
            await this._extraIo();
          }
          continue;
        }

        throw new Error(`getAllRows failed with status: ${bulk.status}`);
      }

      throw new Error(`getAllRows: exceeded ${MAX_IO_RETRIES} IO retries`);
    } finally {
      this._statement.reset();
      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++) {

View on GitHub (pinned to 244cde92a7)