{"record":{"id":"4a80d72b6e60abdf","repo":"remotion-dev/remotion","slug":"failed-to-create-shadows-and-highlights-vertex-arr","errorCode":null,"errorMessage":"Failed to create shadows and highlights vertex array","messagePattern":"Failed to create shadows and highlights vertex array","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/effects/src/shadows-highlights.ts","lineNumber":206,"sourceCode":"\nconst setupShadowsHighlights = (\n\ttarget: HTMLCanvasElement,\n): ShadowsHighlightsState => {\n\tconst gl = target.getContext('webgl2', {\n\t\tpremultipliedAlpha: true,\n\t\talpha: true,\n\t\tpreserveDrawingBuffer: true,\n\t});\n\tif (!gl) {\n\t\tthrow createWebGL2ContextError('shadows and highlights effect');\n\t}\n\n\tgl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);\n\n\tconst program = createProgram(gl);\n\tconst vao = gl.createVertexArray();\n\tif (!vao) {\n\t\tthrow new Error('Failed to create shadows and highlights vertex array');\n\t}\n\n\tconst vbo = gl.createBuffer();\n\tif (!vbo) {\n\t\tthrow new Error('Failed to create shadows and highlights vertex buffer');\n\t}\n\n\tgl.bindVertexArray(vao);\n\tgl.bindBuffer(gl.ARRAY_BUFFER, vbo);\n\tgl.bufferData(\n\t\tgl.ARRAY_BUFFER,\n\t\tnew Float32Array([-1, -1, 0, 0, 1, -1, 1, 0, -1, 1, 0, 1, 1, 1, 1, 1]),\n\t\tgl.STATIC_DRAW,\n\t);\n\n\tconst aPos = gl.getAttribLocation(program, 'aPos');\n\tconst aUv = gl.getAttribLocation(program, 'aUv');\n\tgl.enableVertexAttribArray(aPos);","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/effects/src/shadows-highlights.ts#L188-L224","documentation":"The shadowsHighlights() effect calls gl.createVertexArray() during its setup phase and throws this Error when the WebGL2 context returns null. A null VAO means the GPU driver refused to allocate a vertex array object even though the context itself was successfully acquired. This is an environment-level failure, not a parameter validation error.","triggerScenarios":"Calling shadowsHighlights() in a composition that renders on a machine whose GPU or driver cannot service WebGL2 VAO allocation. Most commonly seen in headless Chromium without GPU passthrough, in CI runners with software rendering, or when too many simultaneous WebGL2 contexts have exhausted the driver's object pool.","commonSituations":"Running Remotion Lambda or Chrome Headless Shell in CI without --enable-features=Vulkan or proper GPU flags; rendering on a VM with no GPU; opening dozens of effects-heavy compositions in parallel tabs that each create their own WebGL2 context; a transient context-loss event immediately after getContext succeeded.","solutions":["Ensure your render environment has GPU acceleration enabled — for headless Chrome pass flags like --use-gl=angle --use-angle=gl --enable-features=Vulkan or run on a runner with a discrete GPU.","Reduce the number of distinct WebGL2-backed effects applied simultaneously; each shadowsHighlights() instance allocates its own context and resources.","Check for WebGL2 context loss by listening to the 'webglcontextlost' event on the canvas before relying on the effect.","If running in Lambda, verify the Chrome binary and layer include GPU-compatible libraries (libGL, libEGL, libGLESv2)."],"exampleFix":"// before — fails on a GPU-less CI runner\nrenderMedia({\n  composition,\n  serveUrl,\n  inputProps,\n});\n\n// after — enable GPU in the headless Chromium used by the renderer\n// (set via puppeteerInstance or renderMedia chromiumOptions)\nrenderMedia({\n  composition,\n  serveUrl,\n  inputProps,\n  chromiumOptions: {\n    enableMultiProcessOnLinux: true,\n    // ensure the headless shell picks up system GL\n  },\n});\n// and launch Chrome with: --use-gl=angle --use-angle=gl","handlingStrategy":"try-catch","validationCode":"// Before applying shadowsHighlights, verify the canvas can get a WebGL2 context\nfunction canAllocateWebGL2(canvas: HTMLCanvasElement): boolean {\n  const gl = canvas.getContext('webgl2', { alpha: true });\n  if (!gl) return false;\n  const testVao = gl.createVertexArray();\n  const ok = testVao !== null;\n  if (testVao) gl.deleteVertexArray(testVao);\n  // Note: getContext returns the same context on repeated calls; do not lose it\n  return ok;\n}","typeGuard":"null","tryCatchPattern":"try {\n  // Apply the effect in a composition\n  shadowsHighlights({ shadows: -0.5 });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('vertex array')) {\n    // WebGL2 resource allocation failed — fall back to no effect or a 2D-canvas approach\n    console.warn('WebGL2 VAO unavailable, skipping shadowsHighlights');\n  } else {\n    throw e;\n  }\n}","preventionTips":["Ensure the render environment has GPU acceleration (headless Chrome flags, Lambda GPU layer).","Limit the number of concurrent WebGL2-backed effects to avoid exhausting the VAO pool.","Handle 'webglcontextlost' events and re-render after context restoration.","Verify GPU drivers are up to date in CI and production environments."],"tags":["webgl2","gpu","resource-allocation","headless","rendering"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}