{"record":{"id":"e8c08c70db0775d9","repo":"AvaloniaUI/Avalonia","slug":"unable-to-access-emscripten-pthread-api","errorCode":null,"errorMessage":"Unable to access emscripten PThread api","messagePattern":"Unable to access emscripten PThread api","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Browser/Avalonia.Browser/webapp/modules/avalonia/rendering/webRenderTargetRegistry.ts","lineNumber":26,"sourceCode":"    private static registry: { [id: number]: ({\n        canvas: HTMLCanvasElement;\n        worker?: Worker;\n    }); } = {};\n\n    private static nextId = 1;\n\n    static create(pthreadId: number, canvas: HTMLCanvasElement, preferredModes: BrowserRenderingMode[]): number {\n        const id = WebRenderTargetRegistry.nextId++;\n        if (pthreadId === 0) {\n            WebRenderTargetRegistry.registry[id] = {\n                canvas\n            };\n            WebRenderTargetRegistry.targets[id] = WebRenderTargetRegistry.createRenderTarget(canvas, preferredModes);\n        } else {\n            const self = globalThis as any;\n            const module = self.Module ?? self.getDotnetRuntime(0)?.Module;\n            const pthreads = module?.PThread;\n            if (pthreads == null) { throw new Error(\"Unable to access emscripten PThread api\"); }\n            const pthread = pthreads.pthreads[pthreadId];\n            if (pthread == null) { throw new Error(`Unable get pthread with id ${pthreadId}`); }\n            let worker: Worker | undefined;\n            if (pthread.postMessage != null) { worker = pthread as Worker; } else { worker = pthread.worker; }\n\n            if (worker == null) { throw new Error(`Unable get Worker for pthread ${pthreadId}`); }\n            const offscreen = canvas.transferControlToOffscreen();\n            worker.postMessage({\n                avaloniaCmd: \"registerCanvas\",\n                canvas: offscreen,\n                modes: preferredModes,\n                id\n            }, [offscreen]);\n            WebRenderTargetRegistry.registry[id] = {\n                canvas,\n                worker\n            };\n        }","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/AvaloniaUI/Avalonia/blob/11c542726898ae954a1ef668c65ec79ec92ab17d/src/Browser/Avalonia.Browser/webapp/modules/avalonia/rendering/webRenderTargetRegistry.ts#L8-L44","documentation":"Thrown by WebRenderTargetRegistry.create when rendering on a worker thread (pthreadId != 0) and the Emscripten Module.PThread API cannot be found. PThread is required because the canvas must be transferred to the owning pthread worker as an OffscreenCanvas via postMessage.","triggerScenarios":"WebRenderTargetRegistry.create(pthreadId, canvas, modes) with a non-zero pthreadId, while globalThis.Module is undefined and getDotnetRuntime(0)?.Module is also missing PThread. Happens when the WASM build was compiled without pthreads support (-pthread) or the runtime is not yet initialized.","commonSituations":"Avalonia WASM build compiled without ENABLE_PTHREADS; runtime not fully booted when a worker-thread render target is requested; running outside the real Avalonia host where Module is absent; mismatched build expecting worker rendering but the artifact lacks pthread support.","solutions":["Build the WASM runtime with pthread support enabled if worker-side rendering is intended.","Route rendering to the main thread (pthreadId === 0 path) when PThread is unavailable.","Wait for runtime ready before registering canvas targets on worker threads.","Verify Module.PThread is present in the browser console at the time of the call."],"exampleFix":"// before\nWebRenderTargetRegistry.create(pthreadId, canvas, [BrowserRenderingMode.WebGL2]);\n\n// after\nconst hasPThreads = globalThis.Module?.PThread ?? globalThis.getDotnetRuntime(0)?.Module?.PThread;\nconst effectivePthread = hasPThreads ? pthreadId : 0;\nWebRenderTargetRegistry.create(effectivePthread, canvas, [BrowserRenderingMode.WebGL2]);","handlingStrategy":"validation","validationCode":"const self = globalThis as any;\nconst module = self.Module ?? self.getDotnetRuntime?.(0)?.Module;\nconst hasPThreads = !!module?.PThread;\nconst effectivePthread = hasPThreads ? pthreadId : 0;","typeGuard":"function hasPThreadApi(): boolean {\n  const self = globalThis as any;\n  const m = self.Module ?? self.getDotnetRuntime?.(0)?.Module;\n  return !!m?.PThread;\n}","tryCatchPattern":"try { return WebRenderTargetRegistry.create(pthreadId, canvas, modes); }\ncatch (e) {\n  if (e instanceof Error && e.message.includes('PThread')) { return WebRenderTargetRegistry.create(0, canvas, modes); }\n  throw e;\n}","preventionTips":["Build WASM with pthread support if worker rendering is intended.","Detect PThread absence and route to the main-thread path.","Wait for runtime ready before registering worker targets."],"tags":["browser","rendering","emscripten","pthread","worker","wasm","typescript"],"backgroundTag":null,"analyzedSha":"11c542726898ae954a1ef668c65ec79ec92ab17d","analyzedAt":"2026-08-13T11:57:40.261Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}