perspective-dev/perspective · error · Error
Init error
Error message
Init error
What it means
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.
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.
Example fix
// before: init sent before wasm resolved
worker.postMessage({ type: "init", clientWorkerURL: wasmUrl, clientWasm: undefined, proxyPort: port });
// after: await all resources first
const wasm = await loadClientWasm();
worker.postMessage({ type: "init", clientWorkerURL: wasmUrl, clientWasm: wasm, proxyPort: port }, [port]); Defensive patterns
Strategy: validation
Validate before calling
function canInitWorker(msg: InitMsg): boolean {
return Boolean(msg.clientWorkerURL) && Boolean(msg.clientWasm) && Boolean(msg.proxyPort);
}
// call before postMessage
if (!canInitWorker(init)) throw new Error("Refusing to init worker: missing clientWorkerURL/clientWasm/proxyPort"); Type guard
function isInitMsgReady(msg: Partial<InitMsg>): msg is InitMsg {
return msg.clientWorkerURL != null && msg.clientWasm != null && msg.proxyPort != null;
} Try / catch
try {
const renderer = await bootstrapWorker(initMsg, port);
} catch (e) {
if (e instanceof Error && e.message === "Init error") {
console.error("Worker init message incomplete — check clientWorkerURL, clientWasm, proxyPort", initMsg);
await retryInitAfterAssetsLoaded();
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Missing perspective-client.wasm
- Missing perspective-client.wasm
- Missing perspective-server.wasm
- Unknown chart tag
- init_server requires a wasm source or a
AI-assisted analysis of perspective-dev/perspective@11c8238c0c (2026-09-09).
Data as JSON: /api/errors/8402231acc4e2b16.
Report an issue: GitHub.
Appendix: source
Thrown at packages/viewer-charts/src/ts/worker/renderer.worker.ts:865
}
/**
* Detect whether this module is loaded in a Web Worker scope.
*/
const IS_WORKER_SCOPE = typeof (globalThis as any).importScripts === "function";
/**
* Worker-mode bootstrap: receives the host's `InitMsg`, instantiates
* wasm, registers fonts, opens a `Client` against the host's
* `ProxySession`, and constructs a {@link WorkerRenderer} bound to the
* supplied control port (which in worker scope is `self`).
*/
async function bootstrapWorker(
msg: InitMsg,
host: MessagePort,
): Promise<WorkerRenderer> {
if (!msg.clientWorkerURL || !msg.clientWasm || !msg.proxyPort) {
throw new Error("Init error");
}
const module = (await import(
msg.clientWorkerURL.toString()
)) as typeof wasm_module_type;
await module.initSync({ module: msg.clientWasm });
// Register every `@font-face` the host found in its document so
// Canvas2D `ctx.font` lookups inside this worker resolve correctly.
if (msg.fontFaces?.length) {
await Promise.all(msg.fontFaces.map(loadFontDeduped));
}
const proxyPort = msg.proxyPort;
const client = new module.Client(
async (proto: Uint8Array) => {
const buf = proto.slice().buffer;View on GitHub (pinned to 11c8238c0c)