{"record":{"id":"77ed506efb5b0b33","repo":"dotnet/AspNetCore.Docs","slug":"worker-exports-not-loaded","errorCode":null,"errorMessage":"worker exports not loaded","messagePattern":"worker exports not loaded","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"aspnetcore/blazor/blazor-with-dotnet-on-web-workers.md","lineNumber":326,"sourceCode":"\n```javascript\nimport { dotnet } from '../_framework/dotnet.js';\n\nlet assemblyExports;\nlet startupError;\n\ntry {\n  const { getAssemblyExports, getConfig } = await dotnet.create();\n  const config = getConfig();\n  assemblyExports = await getAssemblyExports(config.mainAssemblyName);\n} catch (err) {\n  startupError = err.message;\n}\n\nself.addEventListener('message', async e => {\n  try {\n    if (!assemblyExports) {\n      throw new Error(startupError || 'worker exports not loaded');\n    }\n\n    let result;\n    switch (e.data.command) {\n      case 'generateQR':\n        result = assemblyExports.QRGenerator.Generate(e.data.text, e.data.size);\n        break;\n      default:\n        throw new Error(`Unknown command: ${e.data.command}`);\n    }\n\n    self.postMessage({ command: 'response', \n      requestId: e.data.requestId, result });\n  } catch (err) {\n    self.postMessage({ command: 'response', \n      requestId: e.data.requestId, error: err.message });\n  }\n});","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/dotnet/AspNetCore.Docs/blob/c67a80103a1a74db20784debd919c7fdda96c510/aspnetcore/blazor/blazor-with-dotnet-on-web-workers.md#L308-L344","documentation":"The Blazor web-worker script sets `startupError` if `dotnet.create()` / `getAssemblyExports` fails, then in the message handler throws `startupError || 'worker exports not loaded'` when `assemblyExports` is still falsy. The literal fallback fires when the worker never obtained .NET assembly exports and no specific startup error was captured.","triggerScenarios":"Posting a message to the worker before (or after a failed) `dotnet.create()` completed such that `assemblyExports` is null/undefined. Also if `getAssemblyExports` resolved to nothing or the config's mainAssemblyName was wrong.","commonSituations":"Worker boot race — main thread posts `generateQR` before startup finished; missing `[JSExport]` attribute on `Generate`; mainAssemblyName mismatch so `assemblyExports.QRGenerator` is undefined; .NET runtime resource (wasm/dll) failed to fetch (404/CORS).","solutions":["Have the main thread wait for a ready signal from the worker (e.g. worker posts `{command:'ready'}` after `getAssemblyExports`) before sending commands.","Ensure `Generate` is annotated `[JSExport]` and the project sets `<AllowLocation>true</AllowLocation>` / proper runtime assets so exports are discovered.","Verify the published `mainAssemblyName` matches the assembly containing `QRGenerator`.","Inspect `startupError` (post it back in the catch) to distinguish boot failure from a race."],"exampleFix":"// before\nworker.postMessage({ command: 'generateQR', text, size, requestId });\n\n// after\nawait waitForWorkerReady(worker);\nworker.postMessage({ command: 'generateQR', text, size, requestId });","handlingStrategy":"validation","validationCode":"// Main thread: wait for worker ready before sending commands\nfunction postWhenReady(worker, msg) {\n  return new Promise((resolve, reject) => {\n    const onReady = (e) => {\n      if (e.data.command === 'ready') {\n        worker.removeEventListener('message', onReady);\n        worker.postMessage(msg);\n        resolve();\n      }\n    };\n    worker.addEventListener('message', onReady);\n  });\n}","typeGuard":"function workerReady(exports) {\n  return exports != null && typeof exports.QRGenerator?.Generate === 'function';\n}","tryCatchPattern":"// In the worker message handler the throw is intentional; surface it:\nself.postMessage({ command: 'response', requestId: e.data.requestId, error: err.message });\n// Main thread:\nif (resp.error && /worker exports not loaded/.test(resp.error)) retryAfterDelay();","preventionTips":["Send a 'ready' message from the worker after getAssemblyExports resolves.","Annotate exports with `[JSExport]` and verify the main assembly name.","Surface `startupError` to distinguish boot failure from a race."],"tags":["blazor","web-worker","interop","startup","race-condition"],"backgroundTag":null,"analyzedSha":"c67a80103a1a74db20784debd919c7fdda96c510","analyzedAt":"2026-08-13T17:46:11.763Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}