tursodatabase/turso · error · Error
pull() is only available for sync databases
Error message
pull() is only available for sync databases
What it means
pull() waits for remote changes via the native sync engine (waitChanges) and applies them, which only exists for sync databases. The guard requires _isSync, _nativeSyncDb, and _ioContext — all created during connect() when the Database was constructed with a `url`. A local-only database has no sync engine, so pull is meaningless and rejected up front.
Solutions
- Pass url and authToken in the Database options so isSyncConfig() selects the sync path
- Always `await db.connect()` before pull() — it bootstraps the replica and creates the sync handle
- Gate sync calls: `if (db.isSync) await db.pull();` in code paths shared with local-only builds
- Verify a url function option actually returns a string, not null
Example fix
// before
const db = new Database({ path: getDbPath('notes.db') });
await db.connect();
await db.pull(); // throws
// after
const db = new Database({
path: getDbPath('notes.db'),
url: () => remoteUrl, // string or function returning string
authToken,
});
await db.connect();
if (db.isSync) {
await db.pull();
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!db.isSync) {
return; // local-only build: nothing to pull
}
await db.pull(); Type guard
function canPull(db: Database): boolean {
return db.isSync && db.open;
} Try / catch
try {
const changed = await db.pull();
} catch (e) {
if (e instanceof Error && e.message.includes('only available for sync databases')) {
return; // configuration mismatch — skip sync on local-only instances
}
throw e;
} Prevention
- Feature-flag sync calls behind db.isSync rather than try/catching the misuse error
- Ensure the url option (string or function) resolves to a real string before constructing
- Await connect() first; it creates the sync handle and IO context that pull() requires
When it happens
Trigger: Calling await db.pull() on a Database constructed without `url`; calling it on a sync database before `await db.connect()` finished creating the native sync handle and IO context; the url option resolving to null at construction time (url function returning null).
Common situations: Feature-flagged sync where some builds construct the db local-only but shared code still calls pull(); forgetting to await connect() before an initial pull; copying example code for pull() into the local-only quick-start snippet from the README.
Related errors
- push() is only available for sync databases
- checkpoint() is only available for sync databases
- stats() is only available for sync databases
- getAllRows: exceeded
- HTTP request failed: . URL: , Method: , Body size: bytes
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/e3b2d6c2a1416065.
Report an issue: GitHub.
Appendix: 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 244cde92a7)