{"record":{"id":"5d717bf13042def1","repo":"remotion-dev/remotion","slug":"failed-to-create-white-balance-texture","errorCode":null,"errorMessage":"Failed to create white balance texture","messagePattern":"Failed to create white balance texture","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/effects/src/white-balance.ts","lineNumber":170,"sourceCode":"\tgl.attachShader(program, vertexShader);\n\tgl.attachShader(program, fragmentShader);\n\tgl.linkProgram(program);\n\tgl.deleteShader(vertexShader);\n\tgl.deleteShader(fragmentShader);\n\n\tif (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n\t\tconst log = gl.getProgramInfoLog(program);\n\t\tgl.deleteProgram(program);\n\t\tthrow new Error(`White balance shader link failed: ${log ?? '(no log)'}`);\n\t}\n\n\treturn program;\n};\n\nconst createTexture = (gl: WebGL2RenderingContext): WebGLTexture => {\n\tconst texture = gl.createTexture();\n\tif (!texture) {\n\t\tthrow new Error('Failed to create white balance texture');\n\t}\n\n\tgl.bindTexture(gl.TEXTURE_2D, texture);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\tgl.bindTexture(gl.TEXTURE_2D, null);\n\treturn texture;\n};\n\nconst setupWhiteBalance = (target: HTMLCanvasElement): WhiteBalanceState => {\n\tconst gl = target.getContext('webgl2', {\n\t\tpremultipliedAlpha: true,\n\t\talpha: true,\n\t\tpreserveDrawingBuffer: true,\n\t});\n\tif (!gl) {","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/effects/src/white-balance.ts#L152-L188","documentation":"The white balance effect allocates a GPU texture (gl.createTexture) during its one-time WebGL2 setup to hold the source frame it color-corrects. WebGL returns null when the context is lost, the GPU is out of resources, or too many textures/contexts are alive, and the effect treats null as fatal because it cannot sample video frames without it. This is an environment/resource failure, not a parameter mistake: the shader and texture parameters are fixed by the library.","triggerScenarios":"First render frame that applies the whiteBalance() effect on a machine whose WebGL2 context cannot allocate a texture: a lost context (webglcontextlost already fired), GPU memory exhausted, or a headless browser with WebGL disabled. The throw happens inside setupWhiteBalance -> createTexture, before any frame is drawn.","commonSituations":"Headless Chromium in CI/AWS Lambda with software rendering (SwiftShader) misconfigured or GPU blocklisted; too many @remotion/effects compositions mounted at once on one page each grabbing a context; a GPU driver crash or tab suspension that triggered context loss; running in a Remote Desktop / VM without GPU acceleration.","solutions":["Verify WebGL2 is actually available and healthy: in the render browser, check that canvas.getContext('webgl2') returns a non-null context and that gl.isContextLost() is false before relying on the effect.","For headless rendering (Lambda/CI), ensure Chromium launches with GPU/WebGL enabled (e.g. flags enabling SwiftShader) and is not on the GPU blocklist; Remotion's bundled Chrome already does this, so avoid overriding --disable-gpu or --disable-software-rasterizer.","Reduce the number of effects/compositions holding live WebGL contexts concurrently, or render in passes, to stay under the browser's context limit (~16).","Listen for the webglcontextlost event on the render canvas and re-mount the composition to re-create the context and state.","Update GPU drivers / switch from a headless software backend to a real GPU when rendering locally."],"exampleFix":"// before: assumes WebGL2 can always allocate\nimport {whiteBalance} from '@remotion/effects';\n\n// after: feature-detect before mounting effects that need WebGL2\nconst probe = document.createElement('canvas').getContext('webgl2');\nconst supportsEffect = probe != null && !probe.isContextLost();\n\n<VideoEffects effects={supportsEffect ? [whiteBalance({temperature: 0.3})] : []} />","handlingStrategy":"try-catch","validationCode":"// Probe WebGL2 health before mounting whiteBalance\nconst gl = document.createElement('canvas').getContext('webgl2');\nconst webglOk = gl != null && !gl.isContextLost();\nif (!webglOk) {\n  // skip the effect, log, or fall back to a non-WebGL color adjustment\n}","typeGuard":"const hasHealthyWebGL2 = (): boolean => {\n  const gl = document.createElement('canvas').getContext('webgl2');\n  return gl != null && !gl.isContextLost();\n};","tryCatchPattern":"import {whiteBalance} from '@remotion/effects';\n\ntry {\n  return <VideoEffects effects={[whiteBalance({temperature: 0.3})]} />;\n} catch (err) {\n  if (err instanceof Error && err.message.includes('white balance texture')) {\n    // GPU unavailable: render without the effect\n    return <Video />;\n  }\n  throw err;\n}","preventionTips":["Render with Remotion's bundled Chromium (CLI/Lambda) so the software-GPU path is configured correctly.","Do not pass --disable-gpu / --disable-software-rasterizer to the render browser.","Keep the number of concurrently-mounted WebGL effects under the browser context limit (~16).","Listen for webglcontextlost on the render canvas and re-mount the composition to re-create state."],"tags":["webgl","gpu","rendering","headless","context-loss"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}