tursodatabase/turso · error · Error

panic: MainWorker is not initialized

Error message

panic: MainWorker is not initialized

What it means

The vite-dev-hack variant (promise-vite-dev-hack.ts:8) compensates for Vite's dev-server module transforms; init() awaits initThreadPool() and throws if MainWorker (live export of index-vite-dev-hack.js) is still null. As with the sibling variants, MainWorker is only assigned when setupMainThread's callback creates the worker inside that exact module instance, so the throw means no worker was registered where you are checking - duplicate module copies, wrong entry for the bundler, or failed asset loading.

Source

Thrown at bindings/javascript/packages/wasm/promise-vite-dev-hack.ts:8

import { DatabasePromise, DatabaseOpts, SqliteError, Transaction } from "@tursodatabase/database-common"
import { registerFileAtWorker, unregisterFileAtWorker, ioNotifier } from "@tursodatabase/database-wasm-common";
import { initThreadPool, MainWorker, Database as NativeDatabase } from "./index-vite-dev-hack.js";

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

class Database extends DatabasePromise {
    #worker: Worker | null;
    constructor(path: string, opts: DatabaseOpts = {}) {
        super(
            new NativeDatabase(path, opts) as unknown as any,
            () => ioNotifier.waitForCompletion(),
        )
    }
    /**
     * connect database and pre-open necessary files in the OPFS
     */
    override async connect() {
        if (!this.memory) {
            const worker = await init();

View on GitHub (pinned to 244cde92a7)

Solutions

  1. Use the vite-dev-hack entry for Vite dev and await init() before any Database use.
  2. Exclude @tursodatabase/database-wasm from optimizeDeps (or configure it so only one copy is served).
  3. Confirm turso.wasm32-wasi.wasm and worker.js load in the dev-server network tab.
  4. Standardize on a single entry variant across the app and reload fully after config changes.

Example fix

// before
import { Database } from "@tursodatabase/database-wasm/promise-default"; // under vite dev

// after
import { init, Database } from "@tursodatabase/database-wasm/promise-vite-dev-hack";
await init();
Defensive patterns

Strategy: validation

Validate before calling

import { init, Database } from "@tursodatabase/database-wasm/promise-vite-dev-hack";
await init();
const db = new Database("app.db");

Try / catch

try {
  await init();
} catch (e) {
  if (e instanceof Error && /MainWorker is not initialized/.test(e.message)) {
    // Vite dev duplicated or mis-served the module: check optimizeDeps + asset URLs
  } else throw e;
}

Prevention

When it happens

Trigger: Running under Vite dev server but importing a non-hack entry (or production build importing the hack entry), so module transforms split the instance; Vite optimizeDeps re-bundling the wasm package into a second copy; worker.js or turso.wasm not served during dev (import.meta.url base differences); HMR leaving a stale module graph.

Common situations: App works after `vite build` but throws in `vite dev` (or the reverse); upgrading Vite changes asset handling and the wasm/worker URLs 404; multiple components importing different entry variants.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/60fd752eac744675. Report an issue: GitHub.