tursodatabase/turso · error · Error
panic: MainWorker is not initialized
Error message
panic: MainWorker is not initialized
What it means
The default WASM promise entry (promise-default.ts:8) throws this from init() when MainWorker - a live export of index-default.ts assigned inside the setupMainThread callback - is still null after initThreadPool() resolves. index-default.ts uses top-level await (fetches turso.wasm, then setupMainThread spawns the 'turso-database' module worker), so a null MainWorker means the worker-creation callback never ran for the module instance you imported: either a duplicate module copy, an unloaded worker/wasm asset, or calling into the module before its top-level await chain finished.
Source
Thrown at bindings/javascript/packages/wasm/promise-default.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-default.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 = {}) {
const nativeDb = new NativeDatabase(path, opts);
super(
nativeDb as unknown as any,
// In-memory databases use MemoryIO which completes I/O synchronously,
// so there's no OPFS Worker dispatch and the IONotifier would never fire.
// Use undefined (defaults to no-op) so the step loop retries immediately.
(nativeDb as any).memory ? undefined : () => ioNotifier.waitForCompletion(),
)
}
/**
* connect database and pre-open necessary files in the OPFSView on GitHub (pinned to 244cde92a7)
Solutions
- Await init() from the same entry module before any Database construction.
- Verify turso.wasm32-wasi.wasm and worker.js are fetched successfully (network tab) and match the import base.
- Dedupe the package so a single index-default.js instance exists (check the bundler's module graph / lockfile duplicates).
- Load the wasm entry lazily and only in the browser (dynamic import inside useEffect/onMount), never during SSR.
Example fix
// before
import { Database } from "@tursodatabase/database-wasm/promise-default";
export const db = new Database("app.db"); // module-level, init not awaited
// after
import { init, Database } from "@tursodatabase/database-wasm/promise-default";
export async function openDb() {
await init();
return new Database("app.db");
} Defensive patterns
Strategy: validation
Validate before calling
import { init, Database } from "@tursodatabase/database-wasm/promise-default";
export async function openDb(path: string) {
await init(); // resolves only after MainWorker exists
return new Database(path);
} Try / catch
try {
await init();
} catch (e) {
if (e instanceof Error && /MainWorker is not initialized/.test(e.message)) {
// check for duplicate module copies and 404s on turso.wasm32-wasi.wasm / worker.js
} else throw e;
} Prevention
- Never construct Database at module top level; await init() first.
- Dedupe @tursodatabase/database-wasm so index-default.js exists once.
- Confirm wasm/worker assets are served from the correct base path.
- Dynamic-import the entry inside browser-only effects (no SSR).
When it happens
Trigger: Bundler resolving @tursodatabase/database-wasm/promise-default and other deep imports to different physical copies of index-default.js; the browser failing to fetch ./turso.wasm32-wasi.wasm or ./worker.js (404 from wrong base path after bundling); constructing Database in a module that captured the exports before the top-level await settled; running under SSR/jsdom where Worker is missing so setupMainThread silently never assigned MainWorker.
Common situations: Vite/Next.js dev servers re-optimizing and serving two copies of the wasm package; assets not copied to the dist base path; Node SSR prerender importing the browser entry; ad-blockers or CSP blocking worker creation.
Related errors
- panic: MainWorker is not initialized
- panic: MainWorker is not initialized
- panic: MainWorker is not initialized
- method ${name} must be invoked only from the worker thread
- method ${name} must be invoked only from the main thread
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/b9c088abec8f34d7.
Report an issue: GitHub.