tursodatabase/turso · error · Error
sync is disabled as database was opened without sync support
Error message
sync is disabled as database was opened without sync support
What it means
pull() fetches new changes from the remote database, which requires a SyncEngine. When the Database is constructed without a url option the constructor takes the local-only branch (this.#engine = null), so pull() — like push(), checkpoint() and stats() — throws instead of silently doing nothing.
Source
Thrown at bindings/javascript/sync/packages/wasm/promise-turbopack-hack.ts:194
registerFileAtWorker(this.#worker, this.name),
registerFileAtWorker(this.#worker, `${this.name}-wal`),
registerFileAtWorker(this.#worker, `${this.name}-wal-revert`),
registerFileAtWorker(this.#worker, `${this.name}-info`),
registerFileAtWorker(this.#worker, `${this.name}-changes`),
]);
}
await run(this.#runner, this.#engine.connect(), this.execLock);
}
this.connected = true;
}
/**
* pull new changes from the remote database
* if {@link DatabaseOpts.longPollTimeoutMs} is set - then server will hold the connection open until either new changes will appear in the database or timeout occurs.
* @returns true if new changes were pulled from the remote
*/
async pull() {
if (this.#engine == null) {
throw new Error("sync is disabled as database was opened without sync support")
}
const changes = await this.#guards.wait(async () => await run(this.#runner, this.#engine.wait(), this.execLock));
if (changes.empty()) {
return false;
}
await this.#guards.apply(async () => await run(this.#runner, this.#engine.apply(changes), this.execLock));
return true;
}
/**
* push new local changes to the remote database
* if {@link DatabaseOpts.transform} is set - then provided callback will be called for every mutation before sending it to the remote
*/
async push() {
if (this.#engine == null) {
throw new Error("sync is disabled as database was opened without sync support")
}
await this.#guards.push(async () => await run(this.#runner, this.#engine.push(), this.execLock));
}View on GitHub (pinned to bad083fafb)
Solutions
- Pass a url (and usually authToken) in DatabaseOpts: new Database({ path, url: process.env.TURSO_DATABASE_URL!, authToken }) — this is what creates the SyncEngine that pull/push/checkpoint/stats require.
- If the database is intentionally local-only, remove the sync call or gate it behind a 'sync enabled' flag in your own code.
- Verify the environment actually provides the URL in the failing context: log process.env.TURSO_DATABASE_URL at the exact construction site (CI/preview often differ from your machine).
- Use two explicit factory functions — local Database for dev/tests, synced Database for prod — instead of conditionally omitting url on one shared path.
Example fix
// before
const db = new Database({ path: "app.db" }); // no url -> constructor stores #engine = null
await db.pull(); // throws "sync is disabled..."
// after — opt into sync at construction time
const db = new Database({
path: "app.db",
url: process.env.TURSO_DATABASE_URL!, // required for pull/push/checkpoint/stats
authToken: process.env.TURSO_AUTH_TOKEN,
});
await db.connect();
await db.pull(); Defensive patterns
Strategy: validation
Validate before calling
// Decide once, where you build your options, whether sync is on.
const syncOpts = process.env.TURSO_DATABASE_URL
? { url: process.env.TURSO_DATABASE_URL, authToken: process.env.TURSO_AUTH_TOKEN }
: {};
const db = new Database({ path: "app.db", ...syncOpts });
const syncEnabled = Boolean(syncOpts.url);
// Guard every sync call site:
if (syncEnabled) {
await db.connect();
await db.pull(); // likewise push(), checkpoint(), stats()
} Type guard
// #engine is private — there is no runtime probe on the Database instance.
// Narrow your options instead, before construction:
const isSynced = (
opts: DatabaseOpts
): opts is DatabaseOpts & { url: string | (() => string | null) } => opts.url != null; Try / catch
try {
await db.pull();
} catch (e) {
if (e instanceof Error && e.message.startsWith("sync is disabled")) {
// Local-only database: this is a configuration state, not a transient error.
// Skip the sync loop (or alert that this deployment was meant to sync).
return;
}
throw e;
} Prevention
- Treat url as the single switch for sync: never call pull/push/checkpoint/stats on a Database whose options did not include url.
- Construct local (dev/test) and synced (prod) databases through separate explicit factories instead of conditionally omitting url on one shared path.
- Fail fast at startup if a deployment is supposed to sync but TURSO_DATABASE_URL is missing.
- Do not treat connect() succeeding as proof of sync support — connect() also works for local-only databases; only the sync methods throw.
When it happens
Trigger: new Database({ path: 'local.db' }) — DatabaseOpts.url omitted, or explicitly evaluated to undefined (e.g. url: process.env.TURSO_DATABASE_URL ?? undefined with the variable unset) — followed by await db.pull(). The guard is the first statement of pull(), so it fires before any engine work.
Common situations: Sharing one code path between a local-only dev/test database and a synced production database; CI or preview environments missing TURSO_DATABASE_URL so url evaluates to undefined; a refactor that accidentally drops the url field from DatabaseOpts; local (non-sync) WASM demo code later extended with a sync loop.
Related errors
- sync is disabled as database was opened without sync support
- sync is disabled as database was opened without sync support
- remoteWritesExperimental requires a non-null URL
- remoteWritesExperimental requires a non-null URL
- remoteWritesExperimental requires a non-null URL
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/42c6b3a92cb4924b.
Report an issue: GitHub.