tursodatabase/turso · error · Error

pull() is only available for sync databases

Error message

pull() is only available for sync databases

What it means

pull() on the React Native Database waits for remote changes (waitChanges) and applies them locally, using the native sync database and IO context created during a sync-mode connect(). The guard `!this._isSync || !this._nativeSyncDb || !this._ioContext` throws when the database is local-only (no url in opts) or when connect() has not yet created the native sync objects. Like the other sync methods, it is a mode/lifecycle error rather than a network error.

Source

Thrown at bindings/react-native/src/Database.ts:362

   * Push local changes to remote (sync databases only)
   */
  async push(): Promise<void> {
    if (!this._isSync || !this._nativeSyncDb || !this._ioContext) {
      throw new Error('push() is only available for sync databases');
    }

    const operation = this._nativeSyncDb.pushChanges();
    await driveVoidOperation(operation, this._nativeSyncDb, this._ioContext);
  }

  /**
   * Pull remote changes and apply locally (sync databases only)
   *
   * @returns true if changes were applied, false if no changes
   */
  async pull(): Promise<boolean> {
    if (!this._isSync || !this._nativeSyncDb || !this._ioContext) {
      throw new Error('pull() is only available for sync databases');
    }

    // Wait for changes
    const waitOperation = this._nativeSyncDb.waitChanges();
    const changes = await driveChangesOperation(waitOperation, this._nativeSyncDb, this._ioContext);

    // If no changes, return false
    if (!changes) {
      return false;
    }

    // Apply changes
    const applyOperation = this._nativeSyncDb.applyChanges(changes);
    await driveVoidOperation(applyOperation, this._nativeSyncDb, this._ioContext);

    return true;
  }

View on GitHub (pinned to bad083fafb)

Solutions

  1. Construct the Database with `url` (and `authToken`) so isSyncConfig() is true and connect() builds the native sync database
  2. Await connect() — and reuse its promise — before starting any pull loop
  3. If connect() fails, surface the error and re-run connect() before attempting pull() again

Example fix

// before
const db = new Database({ path: 'app.db' });
useEffect(() => { db.pull(); }, []); // throws: local mode

// after
const db = new Database({ path: 'app.db', url, authToken });
await db.connect();
useEffect(() => { db.pull(); }, []);
Defensive patterns

Strategy: validation

Validate before calling

// Reusable lazy-connect gate for sync operations
let connectPromise: Promise<void> | null = null;
function ensureConnected(db: Database): Promise<void> {
  return (connectPromise ??= db.connect());
}

if (isSyncConfig(opts)) {
  await ensureConnected(db);
  const changed = await db.pull();
}

Try / catch

try { await db.pull(); } catch (e) { if (e instanceof Error && e.message.includes('only available for sync databases')) { await db.connect(); return db.pull(); } throw e; }

Prevention

When it happens

Trigger: `new Database({ path })` without url then `await db.pull()`; calling pull() in a startup effect before `await db.connect()` resolves; retrying pull() after a failed connect() without reconnecting.

Common situations: Offline-first flows wired up before sync configuration was added; useEffect-based sync loops racing the initial connect; navigation to a screen that pulls on mount while the DB singleton is still connecting.

Related errors


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