tursodatabase/turso · 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() long-polls the remote database for new changes and applies them locally. The sync WASM Database only constructs a SyncEngine when the constructor receives a url in DatabaseOpts; when opts.url is null or undefined it opens a plain local NativeDatabase and leaves the internal #engine null (promise-default.ts:61-70). Calling pull() on such a local-only instance throws immediately because there is no remote endpoint to wait on.
Solutions
- Pass url (e.g. "libsql://...") together with authToken in DatabaseOpts so the constructor builds the SyncEngine
- If local-only operation is intended, remove the pull() call or gate it behind a sync-enabled check derived from the construction options
- Log DatabaseOpts at construction to confirm url is not undefined (url: undefined fails the opts.url == null check the same as omitting it)
- Check for field-name mismatches such as syncUrl or databaseUrl instead of the expected url key
Example fix
// before
const db = new Database({ path: "local.db" });
await db.pull(); // throws: sync is disabled
// after
const db = new Database({
path: "local.db",
url: "libsql://your-db.turso.io",
authToken: token,
});
await db.connect();
await db.pull(); Defensive patterns
Strategy: validation
Validate before calling
// The engine exists only when url was provided at construction
const opts: DatabaseOpts = { path: "local.db", url, authToken };
const db = new Database(opts);
const syncEnabled = opts.url != null;
if (syncEnabled) {
await db.pull();
} Try / catch
try {
const pulled = await db.pull();
} catch (err) {
if (err instanceof Error && err.message.includes("sync is disabled")) {
// local-only database: nothing to pull, degrade gracefully
return;
}
throw err;
} Prevention
- Always pass url plus authToken in DatabaseOpts when the sync loop (pull/push/checkpoint/stats) will run
- Remember that url: undefined counts as missing because the constructor tests opts.url == null
- Derive a syncEnabled flag from the construction options and guard every sync call with it
- In tests, either supply a remote URL or skip the sync loop instead of calling pull on a local-only database
When it happens
Trigger: Construct with new Database({ path: "local.db" }) (no url field, or url: undefined from a misspelled/destructured config), then call await db.pull(). Any code path that reaches pull() without a remote configured hits this guard.
Common situations: Local dev/test builds omit the sync URL but share the sync-loop code with production; env-driven config where the variable is missing in CI; migrating from the plain WASM database package to the sync package without adding url/authToken options.
Related errors
- sync is disabled as database was opened without sync support
- sync is disabled as database was opened without sync support
- Advanced sync options require a remote embedded replica…
- Advanced sync options require an embedded replica…
- Automatic sync is not supported for embedded replica…
AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-08-20).
Data as JSON: /api/errors/3279e088bf995343.
Report an issue: GitHub.
Appendix: source
Thrown at bindings/javascript/sync/packages/wasm/promise-default.ts:189
await super.connect();
} else {
if (!this.memory) {
const worker = await init();
this.#worker = worker;
await Promise.all(this.#engine.filePaths().map((path: string) => registerFileAtWorker(worker, path)));
}
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 6c72522679)