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

  1. Pass url (e.g. "libsql://...") together with authToken in DatabaseOpts so the constructor builds the SyncEngine
  2. If local-only operation is intended, remove the pull() call or gate it behind a sync-enabled check derived from the construction options
  3. Log DatabaseOpts at construction to confirm url is not undefined (url: undefined fails the opts.url == null check the same as omitting it)
  4. 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

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


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)