tursodatabase/turso · error · Error

method ${name} must be invoked only from the main thread

Error message

method ${name} must be invoked only from the main thread

What it means

panicWorker() in wasm-common/index.ts is the mirror guard of panicMain: the worker-side import object routes methods that only the main thread may execute (async OPFS completion paths and friends) to this throw. If the worker-side shim is invoked on the main thread, the module/imports were wired to the wrong context - normally worker and main import sets are installed in their respective realms by setupMainThread/worker setup. The error therefore indicates main-thread code executing a worker-only import.

Source

Thrown at bindings/javascript/packages/wasm-common/index.ts:42

    is_web_worker(): boolean;
    lookup_file(ptr: number, len: number): number;
    read(handle: number, ptr: number, len: number, offset: number): number;
    read_async(handle: number, ptr: number, len: number, offset: number, c: number);
    write(handle: number, ptr: number, len: number, offset: number): number;
    write_async(handle: number, ptr: number, len: number, offset: number, c: number);
    sync(handle: number): number;
    sync_async(handle: number, c: number);
    truncate(handle: number, len: number): number;
    truncate_async(handle: number, len: number, c: number);
    size(handle: number): number;
}

function panicMain(name): never {
    throw new Error(`method ${name} must be invoked only from the worker thread`);
}

function panicWorker(name): never {
    throw new Error(`method ${name} must be invoked only from the main thread`);
}

function mainImports(worker: Worker, completeOpfs: (c: any, r: any) => void): BrowserImports {
    return {
        is_web_worker(): boolean {
            return false;
        },
        write_async(handle, ptr, len, offset, c) {
            writeFileAtWorker(worker, handle, ptr, len, offset)
                .then(result => {
                    completeOpfs(c, result);
                }, err => {
                    console.error('write_async', err);
                    completeOpfs(c, -1);
                });
        },
        sync_async(handle, c) {
            syncFileAtWorker(worker, handle)

View on GitHub (pinned to 244cde92a7)

Solutions

  1. Use the package's official entry points so worker imports are only installed inside the spawned worker.
  2. Disable worker inlining/single-file transforms for the turso worker chunk in your bundler config.
  3. Never call worker-side helpers (completeOpfs, worker file ops) from application code; treat them as engine internals.
  4. After HMR issues, do a full reload/rebuild - stale module instances can leave shims registered in the wrong realm.
Defensive patterns

Strategy: validation

Validate before calling

const inWorker =
  typeof WorkerGlobalScope !== "undefined" &&
  self instanceof WorkerGlobalScope;
if (!inWorker) {
  // never call worker-side IO/completion helpers here
}

Type guard

const isMainThread = (): boolean =>
  typeof WorkerGlobalScope === "undefined" ||
  !(self instanceof WorkerGlobalScope);

Try / catch

try {
  await operation();
} catch (e) {
  if (e instanceof Error && /invoked only from the main thread/.test(e.message)) {
    // worker imports leaked into the main realm: fix bundler worker config
  } else throw e;
}

Prevention

When it happens

Trigger: Bundling both import sets into one context (e.g. a bundler collapsing the worker into the main chunk) so workerImports end up installed on the main thread; manually calling worker-side completion helpers (e.g. completeOpfs) from main-thread code; duplicated module instances where the worker shim registers in the main realm.

Common situations: Aggressive bundler settings (inlineWorker, single-file builds) merging worker and main code; copy-pasted worker bootstrap into a main-thread script; dev-server HMR re-evaluating the wasm-common module in the main realm.

Related errors


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