{"record":{"id":"f561861aabe383f2","repo":"remotion-dev/remotion","slug":"webgpu-is-not-available-in-this-environment","errorCode":null,"errorMessage":"WebGPU is not available in this environment","messagePattern":"WebGPU is not available in this environment","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/effects/gpu-device.ts","lineNumber":20,"sourceCode":"//\n// `navigator.gpu.requestAdapter()` and `adapter.requestDevice()` are async and\n// non-trivially expensive (~10-100ms on first call). The device is cached\n// globally so every webgpu effect / chain shares the same one.\n//\n// `GPUDevice` is intentionally typed as `unknown` here to avoid pulling\n// `@webgpu/types` into core; effects targeting webgpu narrow the type\n// themselves.\n\nlet devicePromise: Promise<unknown> | null = null;\n\nexport const getGpuDevice = (): Promise<unknown> => {\n\tif (devicePromise) {\n\t\treturn devicePromise;\n\t}\n\n\tdevicePromise = (async () => {\n\t\tif (typeof navigator === 'undefined' || !('gpu' in navigator)) {\n\t\t\tthrow new Error('WebGPU is not available in this environment');\n\t\t}\n\n\t\tconst {gpu} = navigator as unknown as {\n\t\t\tgpu: {requestAdapter: () => Promise<unknown>};\n\t\t};\n\t\tconst adapter = (await gpu.requestAdapter()) as {\n\t\t\trequestDevice: () => Promise<unknown>;\n\t\t} | null;\n\t\tif (!adapter) {\n\t\t\tthrow new Error('No WebGPU adapter available');\n\t\t}\n\n\t\treturn adapter.requestDevice();\n\t})();\n\n\treturn devicePromise;\n};\n","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/core/src/effects/gpu-device.ts#L2-L38","documentation":"getGpuDevice() is the singleton accessor that lazily creates and caches a WebGPU GPUDevice for all webgpu effects. Its first step is to verify navigator.gpu exists; if WebGPU is unavailable (older browser, headless without WebGPU, Node/SSR), it rejects with this error. The cached promise means the same rejection surfaces for all subsequent callers within the session.","triggerScenarios":"Mounting any WebGPU-based effect in an environment without navigator.gpu: non-Chromium browsers without WebGPU, headless Chrome without the WebGPU flag, server-side rendering, or older runtimes.","commonSituations":"Lambda/headless rendering of a composition that uses a WebGPU effect; running in Firefox/Safari without WebGPU enabled; SSR pre-render of a video component.","solutions":["Run in a WebGPU-capable Chromium-based browser/runtime (enable WebGPU in headless via flags).","Replace or guard WebGPU effects with a webgl2 alternative when support is absent.","Feature-detect navigator.gpu before composing the scene and conditionally render the WebGPU effect."],"exampleFix":"// before\nconst supportsWebGPU = true; // assumed\n\n// after\nconst supportsWebGPU = typeof navigator !== 'undefined' && 'gpu' in navigator;\n{supportsWebGPU ? <WebGPUEffect .../> : <WebGL2Effect .../>}","handlingStrategy":"validation","validationCode":"const supportsWebGPU =\n  typeof navigator !== 'undefined' && 'gpu' in navigator;\nif (!supportsWebGPU) {\n  // skip WebGPU effects or use a fallback backend\n}","typeGuard":"const isWebGPUAvailable = (): boolean =>\n  typeof navigator !== 'undefined' && 'gpu' in navigator;","tryCatchPattern":"try {\n  const device = await getGpuDevice();\n} catch (e) {\n  if (String(e?.message).includes('WebGPU is not available')) {\n    // fall back to a webgl2 effect\n  } else throw e;\n}","preventionTips":["Detect WebGPU support before mounting any WebGPU effect.","Keep a non-WebGPU fallback for production rendering targets.","Ensure render environments (Lambda/CI) are WebGPU-capable when needed."],"tags":["effects","webgpu","environment","feature-detection","rendering"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}