{"record":{"id":"ee9c4835a394e2c2","repo":"BabylonJS/Babylon.js","slug":"draco-decoder-module-is-not-available","errorCode":null,"errorMessage":"Draco: Decoder module is not available","messagePattern":"Draco: Decoder module is not available","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/dev/core/src/Meshes/Compression/dracoCompressionWorker.ts","lineNumber":338,"sourceCode":"export function DecoderWorkerFunction(): void {\r\n    let decoderPromise: PromiseLike<DecoderModule> | undefined;\r\n\r\n    onmessage = (event) => {\r\n        const message = event.data;\r\n        switch (message.id) {\r\n            case \"init\": {\r\n                // if URL is provided then load the script. Otherwise expect the script to be loaded already\r\n                if (message.url) {\r\n                    importScripts(message.url);\r\n                }\r\n                const initDecoderObject = message.wasmBinary ? { wasmBinary: message.wasmBinary } : {};\r\n                decoderPromise = DracoDecoderModule(initDecoderObject);\r\n                postMessage({ id: \"initDone\" });\r\n                break;\r\n            }\r\n            case \"decodeMesh\": {\r\n                if (!decoderPromise) {\r\n                    throw new Error(\"Draco: Decoder module is not available\");\r\n                }\r\n                // eslint-disable-next-line github/no-then\r\n                decoderPromise.then((decoder) => {\r\n                    const numPoints = DecodeMesh(\r\n                        decoder,\r\n                        message.dataView,\r\n                        message.attributes,\r\n                        (indices) => {\r\n                            postMessage({ id: \"indices\", data: indices }, [indices.buffer]);\r\n                        },\r\n                        (kind, data, size, offset, stride, normalized) => {\r\n                            postMessage({ id: \"attribute\", kind, data, size, byteOffset: offset, byteStride: stride, normalized }, [data.buffer]);\r\n                        }\r\n                    );\r\n                    postMessage({ id: \"decodeMeshDone\", totalVertices: numPoints });\r\n                });\r\n                break;\r\n            }\r","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/core/src/Meshes/Compression/dracoCompressionWorker.ts#L320-L356","documentation":"In the Draco decoder worker, a \"decodeMesh\" message was received but no decoder module was ever initialized: `decoderPromise` is null. The worker requires an \"init\" message (which calls DracoDecoderModule) before any decode request. The library throws to prevent decoding with an uninitialized WASM decoder.","triggerScenarios":"Posting a {type:\"decodeMesh\"} message to the Draco worker before posting the {type:\"init\"} message, or after init failed silently; also calling DracoCompressionWorker decode paths when the decoder configuration promise never resolved.","commonSituations":"Draco decoder URL misconfigured or blocked (CDN offline, CSP), CustomEXTRA headers/CORS preventing the WASM/JS module load, calling mesh conversion before `dracoCompression.Configuration` with `decoder.available` true, or racing multiple workers where the decode lands in a not-yet-initialized worker.","solutions":["Ensure the Draco decoder is configured before decoding: set `dracoCompression.Configuration.decoder = { url: ... }` (or use default CDN) and await initialization (`DracoCompression.Default...` ready) prior to `_loadGroundAsync`/decode calls.","Verify the decoder URL is reachable (network tab) and CORS/CSP allows the WASM script and .wasm fetch.","Check that the worker receives the init message before decodeMesh; avoid manually posting worker messages out of order.","Catch the worker error and re-queue the decode after re-initializing the decoder module."],"exampleFix":"// before\nmesh.convertToFlatShadedMesh();\nDracoCompression.DecodeMeshToMeshDataAsync(data);\n\n// after\nDracoCompression.Configuration.decoder = { url: \"https://cdn.babylonjs.com/draco/draco_decoder.js\" };\nawait DracoCompression._GetDefaultSupported... // or simply await scene.whenReadyAsync();\nconst meshData = await DracoCompression.DecodeMeshToMeshDataAsync(data);","handlingStrategy":"validation","validationCode":"if (!DracoCompression.Configuration.decoder?.available) {\n    // load decoder config / await initialization before posting decodeMesh\n    throw new SkipDecode();\n}","typeGuard":"function decoderReady(): boolean {\n    return !!DracoCompression.Configuration.decoder?.available;\n}","tryCatchPattern":"try {\n    await worker.decode(view);\n} catch (e) {\n    if (String(e?.message).includes(\"Decoder module is not available\")) {\n        await initDecoder();\n        return worker.decode(view); // retry once after init\n    }\n    throw e;\n}","preventionTips":["Configure the Draco decoder URL at app startup, before loading any compressed assets","Always await decoder initialization before issuing decode requests","Verify decoder URL reachability and CSP/CORS allowances in CI","Never post raw worker messages manually; use the DracoCompression API which orders init/decode correctly"],"tags":["draco","webworker","initialization-order","compression"],"backgroundTag":"module-not-initialized","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}