dotnet/aspnetcore · critical

Worker .NET runtime not loaded

Error message

Worker .NET runtime not loaded

What it means

Thrown by the web-worker message handler when workerExports is empty AND there is no captured startupError. workerExports is only populated by initialize() after the .NET runtime boots; if no successful 'init' message has been processed (or it is still in flight), the worker has no callable exports and rejects with this generic message.

Source

Thrown at src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWebWorker-CSharp/wwwroot/dotnet-web-worker.js:35

    } catch (err) {
        const errorMessage = err?.message ?? String(err);
        startupError = errorMessage;
        console.error("[Worker] Failed to initialize .NET:", err);
        self.postMessage({ type: "ready", error: errorMessage });
    }
}

self.addEventListener('message', async (e) => {
    if (e.data.type === 'init') {
        await initialize(e.data.dotnetJsUrl, e.data.assemblyName);
        return;
    }

    const { method, args, requestId } = e.data;

    try {
        if (Object.keys(workerExports).length === 0) {
            throw new Error(startupError || "Worker .NET runtime not loaded");
        }

        const fn = method.split('.').reduce((obj, part) => obj?.[part], workerExports);
        if (typeof fn !== 'function') {
            throw new Error(`Method not found: ${method}`);
        }

        const result = await fn(...args);
        self.postMessage({ type: "result", requestId, result }, collectTransferables(result));
    } catch (err) {
        self.postMessage({ type: "result", requestId, error: err?.message ?? String(err) });
    }
});

function collectTransferables(value) {
    if (ArrayBuffer.isView(value)) return [value.buffer];
    if (value instanceof ArrayBuffer) return [value];
    return [];

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. Always send {type:'init', dotnetJsUrl, assemblyName} first and await the worker's {type:'ready'} reply before invoking methods.
  2. If ready includes an error, surface it and do not call further; re-init after fixing the URL.
  3. Verify dotnetJsUrl resolves under the worker's origin and CORS/service-worker allow it.
  4. Queue calls until ready rather than firing immediately.

Example fix

// before
worker.postMessage({ type:'init', dotnetJsUrl, assemblyName });
worker.postMessage({ method:'DoThing', args:[], requestId:1 }); // race

// after
worker.addEventListener('message', function ready(e){
  if (e.data.type==='ready' && !e.data.error) {
    worker.removeEventListener('message', ready);
    worker.postMessage({ method:'DoThing', args:[], requestId:1 });
  }
});
worker.postMessage({ type:'init', dotnetJsUrl, assemblyName });
Defensive patterns

Strategy: validation

Validate before calling

function assertWorkerReady(exports) {
  if (!exports || Object.keys(exports).length === 0) throw new Error('Worker not initialized; send init and await ready first.');
}

Type guard

function isWorkerReady(e: any): boolean { return e && typeof e === 'object' && Object.keys(e).length > 0; }

Try / catch

null

Prevention

When it happens

Trigger: Posting a method-invocation message to the worker before posting the 'init' message; the init message failed with an empty error message (err.message undefined); a race where init is asynchronous and the caller dispatches immediately after postMessage('init').

Common situations: Caller forgetting the init handshake; race between posting init and the first call; the dotnetJsUrl is wrong so import() rejects but the error message is empty; worker loaded from a cold cache where getAssemblyExports returns nothing.

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/6884e303126c7c9e. Report an issue: GitHub.