{"record":{"id":"8402231acc4e2b16","repo":"perspective-dev/perspective","slug":"init-error","errorCode":null,"errorMessage":"Init error","messagePattern":"Init error","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/viewer-charts/src/ts/worker/renderer.worker.ts","lineNumber":865,"sourceCode":"}\n\n/**\n * Detect whether this module is loaded in a Web Worker scope.\n */\nconst IS_WORKER_SCOPE = typeof (globalThis as any).importScripts === \"function\";\n\n/**\n * Worker-mode bootstrap: receives the host's `InitMsg`, instantiates\n * wasm, registers fonts, opens a `Client` against the host's\n * `ProxySession`, and constructs a {@link WorkerRenderer} bound to the\n * supplied control port (which in worker scope is `self`).\n */\nasync function bootstrapWorker(\n    msg: InitMsg,\n    host: MessagePort,\n): Promise<WorkerRenderer> {\n    if (!msg.clientWorkerURL || !msg.clientWasm || !msg.proxyPort) {\n        throw new Error(\"Init error\");\n    }\n\n    const module = (await import(\n        msg.clientWorkerURL.toString()\n    )) as typeof wasm_module_type;\n\n    await module.initSync({ module: msg.clientWasm });\n\n    // Register every `@font-face` the host found in its document so\n    // Canvas2D `ctx.font` lookups inside this worker resolve correctly.\n    if (msg.fontFaces?.length) {\n        await Promise.all(msg.fontFaces.map(loadFontDeduped));\n    }\n\n    const proxyPort = msg.proxyPort;\n    const client = new module.Client(\n        async (proto: Uint8Array) => {\n            const buf = proto.slice().buffer;","sourceCodeStart":847,"sourceCodeEnd":883,"githubUrl":"https://github.com/perspective-dev/perspective/blob/11c8238c0c9c0b9e490b5a6a2dc277afad1e980a/packages/viewer-charts/src/ts/worker/renderer.worker.ts#L847-L883","documentation":"bootstrapWorker validates that the InitMsg carries every resource needed to start the wasm-backed renderer: clientWorkerURL, clientWasm, and proxyPort. If any of the three is missing/null/empty it throws a generic 'Init error'. This is a fail-fast guard so the worker never proceeds with a half-initialized wasm module.","triggerScenarios":"Posting an InitMsg to the worker where any of msg.clientWorkerURL, msg.clientWasm, or msg.proxyPort is undefined, null, or an empty string — e.g. the host constructed the init message before the wasm finished loading, or forgot to transfer the MessagePort.","commonSituations":"Race between asset loading (wasm URL fetch) and worker boot; config object partially populated because a build/CDN step dropped the wasm asset; caller forgot to pass the proxy MessagePort in the init message; async init code that resolves the init promise before all fields are set.","solutions":["Inspect the InitMsg at the sender: log clientWorkerURL, clientWasm, and proxyPort before postMessage and fix whichever is missing.","Ensure all init fields are resolved (URLs fetched, wasm bytes available, MessagePort acquired) BEFORE constructing/posting the init message.","Check build/asset pipeline that the wasm and client worker assets are emitted and reachable, not just referenced.","Verify the MessagePort is transferred in the postMessage transfer list and not closed beforehand.","Wrap worker bootstrap in a retry/await so a slow asset load completes before init."],"exampleFix":"// before: init sent before wasm resolved\nworker.postMessage({ type: \"init\", clientWorkerURL: wasmUrl, clientWasm: undefined, proxyPort: port });\n// after: await all resources first\nconst wasm = await loadClientWasm();\nworker.postMessage({ type: \"init\", clientWorkerURL: wasmUrl, clientWasm: wasm, proxyPort: port }, [port]);","handlingStrategy":"validation","validationCode":"function canInitWorker(msg: InitMsg): boolean {\n  return Boolean(msg.clientWorkerURL) && Boolean(msg.clientWasm) && Boolean(msg.proxyPort);\n}\n// call before postMessage\nif (!canInitWorker(init)) throw new Error(\"Refusing to init worker: missing clientWorkerURL/clientWasm/proxyPort\");","typeGuard":"function isInitMsgReady(msg: Partial<InitMsg>): msg is InitMsg {\n  return msg.clientWorkerURL != null && msg.clientWasm != null && msg.proxyPort != null;\n}","tryCatchPattern":"try {\n  const renderer = await bootstrapWorker(initMsg, port);\n} catch (e) {\n  if (e instanceof Error && e.message === \"Init error\") {\n    console.error(\"Worker init message incomplete — check clientWorkerURL, clientWasm, proxyPort\", initMsg);\n    await retryInitAfterAssetsLoaded();\n  } else throw e;\n}","preventionTips":["Await all asset loads (wasm bytes, worker URL) before posting the init message.","Always include the MessagePort in the postMessage transfer list.","Add an assertion/log of the init payload right before postMessage during development.","Verify the build pipeline emits and serves the wasm/client-worker assets.","Centralize init-message construction in one typed factory so fields can't be forgotten."],"tags":["worker","initialization","missing-argument","webassembly"],"backgroundTag":"missing-required-argument","analyzedSha":"11c8238c0c9c0b9e490b5a6a2dc277afad1e980a","analyzedAt":"2026-09-09T00:35:19.377Z","contentChangedAt":"2026-09-09T00:35:19.377Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}