tursodatabase/turso · error

remoteWritesExperimental requires a non-null URL

Error message

remoteWritesExperimental requires a non-null URL

What it means

When remoteWritesExperimental is enabled, the constructor must resolve a concrete remote URL before creating the RemoteWriter. url may be supplied as a lazy resolver function; if calling it yields null or undefined, resolveUrl fails fast (promise-turbopack-hack.ts:41-50) because a null endpoint must never reach the writer. A plain-string url is accepted as-is and never triggers this error.

Solutions

  1. Set the environment/config value the resolver reads so it is non-null in every build mode where remoteWritesExperimental is on
  2. Pass the URL as a plain string when it is already known at construction time
  3. Enable remoteWritesExperimental conditionally, only after your own code confirms the resolved URL is non-null

Example fix

// before
const db = new Database({
  path: "local.db",
  remoteWritesExperimental: true,
  url: () => import.meta.env.VITE_TURSO_DB_URL, // undefined in prod
});

// after
const url = import.meta.env.VITE_TURSO_DB_URL;
if (url == null) throw new Error("VITE_TURSO_DB_URL is not set");
const db = new Database({
  path: "local.db",
  remoteWritesExperimental: true,
  url,
});
Defensive patterns

Strategy: validation

Validate before calling

// Resolve and verify before enabling remote writes
const resolvedUrl = typeof url === "function" ? url() : url;
if (resolvedUrl == null) {
  throw new Error("remoteWritesExperimental needs a URL; check your env/config");
}
const db = new Database({
  path,
  url: resolvedUrl,
  authToken,
  remoteWritesExperimental: true,
});

Try / catch

try {
  const db = new Database({ path, url, remoteWritesExperimental: true });
} catch (err) {
  if (err instanceof Error && err.message.includes("non-null URL")) {
    // URL resolver returned null: fail startup with a clearer message
    throw new Error("Missing remote database URL (check env vars for this build mode)");
  }
  throw err;
}

Prevention

When it happens

Trigger: new Database({ ..., remoteWritesExperimental: true, url: () => import.meta.env.VITE_TURSO_DB_URL }) where the variable is undefined in some build mode; a resolver reading asynchronously-loaded config before it is ready; a placeholder () => null that was never replaced.

Common situations: Environment variable defined for dev but missing in preview/production builds; config fetched at runtime so the resolver runs before the value exists; bundler-specific entry (turbopack dev server) where env injection behaves differently than expected.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-08-20). Data as JSON: /api/errors/c9c293c8444621ad. Report an issue: GitHub.

Appendix: source

Thrown at bindings/javascript/sync/packages/wasm/promise-turbopack-hack.ts:45

        async write(path: string, data: Buffer | Uint8Array): Promise<void> {
            values.set(path, data);
        }
    }
};

async function init(): Promise<Worker> {
    await initThreadPool();
    if (MainWorker == null) {
        throw new Error("panic: MainWorker is not initialized");
    }
    return MainWorker;
}

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 {
    #runner: Runner;
    #engine: any;
    #io: ProtocolIo;
    #guards: SyncEngineGuards;
    #worker: Worker | 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(

View on GitHub (pinned to 6c72522679)