{"record":{"id":"6884e303126c7c9e","repo":"dotnet/aspnetcore","slug":"worker-net-runtime-not-loaded","errorCode":null,"errorMessage":"Worker .NET runtime not loaded","messagePattern":"Worker \\.NET runtime not loaded","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWebWorker-CSharp/wwwroot/dotnet-web-worker.js","lineNumber":35,"sourceCode":"    } catch (err) {\n        const errorMessage = err?.message ?? String(err);\n        startupError = errorMessage;\n        console.error(\"[Worker] Failed to initialize .NET:\", err);\n        self.postMessage({ type: \"ready\", error: errorMessage });\n    }\n}\n\nself.addEventListener('message', async (e) => {\n    if (e.data.type === 'init') {\n        await initialize(e.data.dotnetJsUrl, e.data.assemblyName);\n        return;\n    }\n\n    const { method, args, requestId } = e.data;\n\n    try {\n        if (Object.keys(workerExports).length === 0) {\n            throw new Error(startupError || \"Worker .NET runtime not loaded\");\n        }\n\n        const fn = method.split('.').reduce((obj, part) => obj?.[part], workerExports);\n        if (typeof fn !== 'function') {\n            throw new Error(`Method not found: ${method}`);\n        }\n\n        const result = await fn(...args);\n        self.postMessage({ type: \"result\", requestId, result }, collectTransferables(result));\n    } catch (err) {\n        self.postMessage({ type: \"result\", requestId, error: err?.message ?? String(err) });\n    }\n});\n\nfunction collectTransferables(value) {\n    if (ArrayBuffer.isView(value)) return [value.buffer];\n    if (value instanceof ArrayBuffer) return [value];\n    return [];","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWebWorker-CSharp/wwwroot/dotnet-web-worker.js#L17-L53","documentation":"Thrown inside the worker's message listener when workerExports is empty at the time a method-call message arrives. workerExports is populated only after the .NET runtime (via the supplied dotnetJsUrl) successfully initializes the assembly and GETs its exports. An empty exports map means initialization never completed or failed; startupError holds the original init error if one exists.","triggerScenarios":"Posting a {method,args,requestId} message to the worker before posting the {type:'init', dotnetJsUrl, assemblyName} message; posting it after init failed (bad dotnetJsUrl, 404 on assemblies, WASM not supported); or a race where the method-call message arrives before initialize() resolves.","commonSituations":"Mis-ordered worker startup sequence from the host page; CDN/dotnetJsUrl returns 404; the assemblyName doesn't match a real assembly; the worker is reused after a failed init without re-initializing; browser blocks WASM via CSP.","solutions":["Ensure the host posts the 'init' message first and awaits its acknowledgment before dispatching method calls.","Inspect startupError (it is forwarded when set) — the underlying .NET runtime load error is the real cause.","Verify dotnetJsUrl is reachable (network tab) and that assemblyName exactly matches the WASM build output.","Check the worker's Content-Security-Policy allows 'wasm-unsafe-eval' and script-src for the runtime URL.","Re-send an init message to retry; the current message will be rejected but a subsequent call will succeed once exports are populated."],"exampleFix":"// before\nself.postMessage({ method, args, requestId }); // before init → throws\n\n// after — host waits for ready signal\nconst ready = new Promise(resolve => {\n  worker.addEventListener('message', e => { if (e.data.type === 'ready') resolve(); });\n});\nworker.postMessage({ type: 'init', dotnetJsUrl, assemblyName });\nawait ready;\nworker.postMessage({ method, args, requestId });","handlingStrategy":"validation","validationCode":"// Host-side: track init state before posting method calls\nlet initialized = false, initError: string | null = null;\nworker.addEventListener('message', e => {\n  if (e.data.type === 'ready') initialized = true;\n  if (e.data.type === 'init-error') initError = e.data.error;\n});\nfunction assertReady() {\n  if (initError) throw new Error(`Worker init failed: ${initError}`);\n  if (!initialized) throw new Error('Worker not initialized — send init first');\n}","typeGuard":"type InitMessage = { type: 'init'; dotnetJsUrl: string; assemblyName: string };\ntype CallMessage = { method: string; args: unknown[]; requestId: string };\nfunction isInitMessage(m: unknown): m is InitMessage {\n  return (m as any)?.type === 'init';\n}","tryCatchPattern":"// inside the worker's message listener:\nif (Object.keys(workerExports).length === 0) {\n  self.postMessage({ type: 'result', requestId, error: startupError || 'Worker .NET runtime not loaded' });\n  return; // do not throw — surface to caller gracefully\n}","preventionTips":["Always send the 'init' message before any method-call message.","Surface a ready/error signal from the worker before the host sends calls.","Verify dotnetJsUrl and assemblyName at host build time."],"tags":["web-worker","blazor","wasm","initialization","race-condition"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}