{"record":{"id":"c9c293c8444621ad","repo":"tursodatabase/turso","slug":"remotewritesexperimental-requires-a-non-null-url-c9c293","errorCode":null,"errorMessage":"remoteWritesExperimental requires a non-null URL","messagePattern":"remoteWritesExperimental requires a non-null URL","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"bindings/javascript/sync/packages/wasm/promise-turbopack-hack.ts","lineNumber":45,"sourceCode":"        async write(path: string, data: Buffer | Uint8Array): Promise<void> {\n            values.set(path, data);\n        }\n    }\n};\n\nasync function init(): Promise<Worker> {\n    await initThreadPool();\n    if (MainWorker == null) {\n        throw new Error(\"panic: MainWorker is not initialized\");\n    }\n    return MainWorker;\n}\n\nfunction resolveUrl(url: string | (() => string | null)): string {\n    if (typeof url === \"function\") {\n        const resolved = url();\n        if (resolved == null) {\n            throw new Error(\"remoteWritesExperimental requires a non-null URL\");\n        }\n        return resolved;\n    }\n    return url;\n}\n\nclass Database extends DatabasePromise {\n    #runner: Runner;\n    #engine: any;\n    #io: ProtocolIo;\n    #guards: SyncEngineGuards;\n    #worker: Worker | null;\n    #remoteWriter: RemoteWriter | null = null;\n    #db: any;\n    constructor(opts: DatabaseOpts) {\n        if (opts.url == null) {\n            const db = new NativeDatabase(opts.path, { tracing: opts.tracing, experimental: opts.experimental }) as any;\n            super(","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/bindings/javascript/sync/packages/wasm/promise-turbopack-hack.ts#L27-L63","documentation":"Thrown from the Database constructor when remoteWritesExperimental is enabled and the url option is a lazy provider function (url?: string | (() => string | null)) whose call returns null. The RemoteWriter that forwards writes to the remote server needs a concrete URL at construction time, so resolveUrl() invokes the provider immediately and throws when it yields null. Note the asymmetry: the plain sync engine tolerates a null lazy URL (it just sets bootstrapIfEmpty: false), but remoteWritesExperimental does not.","triggerScenarios":"new Database({ path, remoteWritesExperimental: true, url: () => process.env.TURSO_DATABASE_URL ?? null }) where the env var (or any config store the closure reads) is unset at the moment the Database is constructed. Any lazy provider returning null (KV read, platform binding attached late, not-yet-loaded config) hits the same path. The same code ships as promise-bundle.ts, promise-default.ts and promise-turbopack-hack.ts, so the stack trace names whichever bundler entry point your build selected.","commonSituations":"Next.js/Vite/Turbopack builds where process.env values are inlined at build time and the variable was not configured for that environment; serverless/edge runtimes where the binding is attached after module evaluation; CI or preview deployments missing the TURSO_DATABASE_URL secret; copy-pasting the lazy-url pattern from plain-sync examples while also enabling remoteWritesExperimental.","solutions":["Make sure the URL source is populated before construction: set TURSO_DATABASE_URL in the environment (or .env.local) for the failing environment and confirm it is present at runtime, not only on your machine.","Pass a concrete string URL when using remoteWritesExperimental: url: process.env.TURSO_DATABASE_URL! (assert non-null yourself before constructing).","Enable remote writes conditionally: remoteWritesExperimental: url != null, so construction degrades to local/sync-only mode instead of crashing when the URL is absent.","If you intended offline-first lazy sync without remote writes, remove remoteWritesExperimental; the sync engine alone accepts a null lazy URL."],"exampleFix":"// before — throws in the constructor when the env var is missing at that moment\nconst db = new Database({\n    path: \"app.db\",\n    remoteWritesExperimental: true,\n    url: () => process.env.TURSO_DATABASE_URL ?? null,\n});\n\n// after — resolve the URL first, enable remote writes only when it exists\nconst url = process.env.TURSO_DATABASE_URL ?? null;\nconst db = new Database({\n    path: \"app.db\",\n    url: url ?? undefined,\n    remoteWritesExperimental: url != null,\n});","handlingStrategy":"validation","validationCode":"import { Database, type DatabaseOpts } from \"@tursodatabase/sync-wasm\";\n\nconst lazyUrl = () => process.env.TURSO_DATABASE_URL ?? null;\n\n// Resolve the URL BEFORE constructing so the constructor can never throw.\nconst url = lazyUrl();\nconst opts: DatabaseOpts = {\n    path: \"app.db\",\n    url: url ?? undefined,\n    remoteWritesExperimental: url != null, // commit to remote writes only with a real URL\n};\nconst db = new Database(opts);","typeGuard":"type LazyUrl = string | (() => string | null);\n\nfunction resolvesToUrl(url: LazyUrl | undefined): url is string {\n    if (typeof url === \"string\") return url.length > 0;\n    if (typeof url === \"function\") return url() != null;\n    return false;\n}\n\n// use: if (resolvesToUrl(opts.url)) { /* safe to set remoteWritesExperimental */ }","tryCatchPattern":"try {\n    db = new Database({ path, remoteWritesExperimental: true, url: lazyUrl });\n} catch (e) {\n    if (e instanceof Error && e.message.includes(\"requires a non-null URL\")) {\n        // Configuration problem, not a transient failure — fail loudly with context.\n        throw new Error(\"TURSO_DATABASE_URL must be set before enabling remoteWritesExperimental\");\n    }\n    throw e; // never swallow unrelated constructor errors\n}","preventionTips":["Never combine remoteWritesExperimental with an unguarded lazy url provider; resolve the value first and pass a plain string.","Assert required environment variables at process start (fail fast) instead of inside closures evaluated at construction time.","Remember bundler variants: the identical error surfaces from promise-bundle.ts, promise-default.ts or promise-turbopack-hack.ts depending on your bundler.","Add a CI test that constructs the Database with remoteWritesExperimental in an environment without the URL variable, so a missing-secret crash is caught before deploy."],"tags":["wasm","sync","remote-writes","configuration","environment-variables","constructor"],"backgroundTag":"missing-env-var","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}