tursodatabase/turso · error · Error

remoteWritesExperimental requires a non-null URL

Error message

remoteWritesExperimental requires a non-null URL

What it means

When remoteWritesExperimental is enabled and opts.url is a function (a lazy URL provider), the Database constructor calls resolveUrl() to get a concrete URL for the RemoteWriter. If invoking the provider returns null or undefined — the convention for 'no remote available' — construction fails with this error, because the remote writer cannot operate without a URL. It happens synchronously in the constructor, before any sync starts.

Source

Thrown at bindings/javascript/sync/packages/native/promise.ts:48

};

function memoryIO(): ProtocolIo {
    let values = new Map();
    return {
        async read(path: string): Promise<Buffer | Uint8Array | null> {
            return values.get(path);
        },
        async write(path: string, data: Buffer | Uint8Array): Promise<void> {
            values.set(path, data);
        }
    }
};

function resolveUrl(url: string | (() => string | null)): string {
    if (typeof url === "function") {
        const resolved = url();
        if (resolved == null) {
            throw new Error("remoteWritesExperimental requires a non-null URL");
        }
        return resolved;
    }
    return url;
}

class Database extends DatabasePromise {
    #engine: any;
    #guards: SyncEngineGuards | null = null;
    #runner: Runner | null = null;
    #remoteWriter: RemoteWriter | null = null;
    #db: any;
    constructor(opts: DatabaseOpts) {
        if (opts.url == null) {
            const db = new NativeDatabase(opts.path, { tracing: opts.tracing, experimental: opts.experimental }) as any;
            super(db);
            this.#db = db;
            this.#engine = null;

View on GitHub (pinned to bad083fafb)

Solutions

  1. Ensure the URL provider returns a non-null string before enabling remoteWritesExperimental (resolve it once up front and pass the string).
  2. Defer constructing this Database until the URL is known/provisioned.
  3. If remote writes are optional, construct without remoteWritesExperimental when the provider yields null.

Example fix

// before
new Database({ path, url: () => tenantUrl, remoteWritesExperimental: true }); // tenantUrl is null here

// after
const url = typeof urlOpt === 'function' ? urlOpt() : urlOpt;
if (url == null) {
  throw new Error('sync URL not available yet; construct this database after provisioning');
}
new Database({ path, url, remoteWritesExperimental: true });
Defensive patterns

Strategy: validation

Validate before calling

const resolvedUrl = typeof urlOpt === 'function' ? urlOpt() : urlOpt;
if (resolvedUrl == null) {
  throw new Error('sync URL unavailable; defer remoteWritesExperimental until it is provisioned');
}
const db = new Database({ path, url: resolvedUrl, remoteWritesExperimental: true });

Type guard

const isResolvedUrl = (u: unknown): u is string => typeof u === 'string' && u.length > 0;

Prevention

When it happens

Trigger: new Database({ path, url: () => currentUrl, remoteWritesExperimental: true }) where currentUrl is null at construction time (not yet provisioned, feature flag off, offline fallback); a URL provider that reads per-tenant config which is missing for the first request; passing a function that returns undefined due to a typo in the property it reads.

Common situations: Multi-tenant apps resolving the sync URL lazily per database; environments where the sync URL env var is missing in dev but present in prod; gradual rollouts that disable sync by returning null from the provider.

Related errors


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