AvaloniaUI/Avalonia · error · Error

Unable get Worker for pthread ${pthreadId}

Error message

Unable get Worker for pthread ${pthreadId}

What it means

Thrown by WebRenderTargetRegistry.create when the located pthread object exposes neither a postMessage method nor a .worker property. The code needs a Worker reference to call transferControlToOffscreen and postMessage the canvas to the thread; without a worker handle the transfer is impossible.

Source

Thrown at src/Browser/Avalonia.Browser/webapp/modules/avalonia/rendering/webRenderTargetRegistry.ts:32

    static create(pthreadId: number, canvas: HTMLCanvasElement, preferredModes: BrowserRenderingMode[]): number {
        const id = WebRenderTargetRegistry.nextId++;
        if (pthreadId === 0) {
            WebRenderTargetRegistry.registry[id] = {
                canvas
            };
            WebRenderTargetRegistry.targets[id] = WebRenderTargetRegistry.createRenderTarget(canvas, preferredModes);
        } else {
            const self = globalThis as any;
            const module = self.Module ?? self.getDotnetRuntime(0)?.Module;
            const pthreads = module?.PThread;
            if (pthreads == null) { throw new Error("Unable to access emscripten PThread api"); }
            const pthread = pthreads.pthreads[pthreadId];
            if (pthread == null) { throw new Error(`Unable get pthread with id ${pthreadId}`); }
            let worker: Worker | undefined;
            if (pthread.postMessage != null) { worker = pthread as Worker; } else { worker = pthread.worker; }

            if (worker == null) { throw new Error(`Unable get Worker for pthread ${pthreadId}`); }
            const offscreen = canvas.transferControlToOffscreen();
            worker.postMessage({
                avaloniaCmd: "registerCanvas",
                canvas: offscreen,
                modes: preferredModes,
                id
            }, [offscreen]);
            WebRenderTargetRegistry.registry[id] = {
                canvas,
                worker
            };
        }
        return id;
    }

    static initializeWorker() {
        const oldHandler = self.onmessage;
        self.onmessage = ev => {

View on GitHub (pinned to 11c5427268)

Solutions

  1. Verify the Emscripten version matches what the interop code expects for the pthread.worker field.
  2. Defer registration until the pthread worker is fully attached.
  3. Fall back to main-thread (pthreadId === 0) rendering if no worker handle can be obtained.
  4. Inspect the pthread object keys in the console to find the correct worker accessor.

Example fix

// before
if (pthread.postMessage != null) { worker = pthread as Worker; } else { worker = pthread.worker; }
if (worker == null) throw new Error(`Unable get Worker for pthread ${pthreadId}`);

// after
const worker = pthread.postMessage ? pthread : pthread.worker ?? pthread._worker;
if (!worker) { /* fall back to main-thread rendering */ return WebRenderTargetRegistry.create(0, canvas, modes); }
Defensive patterns

Strategy: fallback

Validate before calling

const pthread = pthreads.pthreads[pthreadId];
const worker = pthread?.postMessage ? pthread : pthread?.worker;
if (!worker) { /* fall back to main-thread rendering (pthreadId 0) */ }

Type guard

function hasWorkerHandle(pthreadId: number): boolean {
  const self = globalThis as any;
  const pthreads = (self.Module ?? self.getDotnetRuntime?.(0)?.Module)?.PThread?.pthreads;
  const p = pthreads?.[pthreadId];
  return !!(p && (p.postMessage || p.worker));
}

Try / catch

try { return WebRenderTargetRegistry.create(pthreadId, canvas, modes); }
catch (e) {
  if (e instanceof Error && /Worker for pthread/.test(e.message)) { return WebRenderTargetRegistry.create(0, canvas, modes); }
  throw e;
}

Prevention

When it happens

Trigger: pthread.postMessage is null and pthread.worker is also null/undefined. Can happen with an unexpected Emscripten PThread object shape, a partially-initialized pthread, or a version mismatch between the interop assumptions and the actual Emscripten runtime internals.

Common situations: Emscripten version change altering the pthread object shape; a pthread in a transitional state (allocated struct but worker not yet attached); custom/patched runtime where the worker field has a different name.

Related errors


AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13). Data as JSON: /api/errors/caa10ef44bf7ca21. Report an issue: GitHub.