remotion-dev/remotion · error · Error

Failed to upload the 3D LUT texture

Error message

Failed to upload the 3D LUT texture (WebGL error ${uploadError})

What it means

After uploading the parsed LUT data with gl.texImage3D(..., gl.FLOAT, parsed.data), applyLut() polls gl.getError(). Any value other than gl.NO_ERROR means the GPU rejected the 3D texture upload, and the library throws with the raw WebGL error enum in the message. Unlike the size check ([25]), this catches failures at the actual upload (out-of-memory, invalid data, driver limits).

Solutions

  1. Decode the numeric error in the message: 0x505 (OUT_OF_MEMORY) means the LUT is too big - use a smaller LUT_3D_SIZE
  2. Verify the .cube data row count matches LUT_3D_SIZE^3 (a mismatched file produces INVALID_VALUE)
  3. Reduce LUT size (e.g. 33 -> 17) to lower memory requirements
  4. Check gl.isContextLost(); if lost, recreate the context and re-run setupLut
  5. Update GPU drivers/browser if float-texture upload fails on a known-good LUT

Example fix

// before: oversized LUT on mobile
const lut = parseCubeLut(lutSize64File);
applyLut(state, lut);
// after: fall back to a smaller resampled LUT
const lut = parseCubeLut(lutSize64File);
const safe = state.gl.getParameter(state.gl.MAX_3D_TEXTURE_SIZE) < 64
  ? downsampleCubeLut(lut, 32)
  : lut;
applyLut(state, safe);
Defensive patterns

Strategy: try-catch

Validate before calling

const expected = size * size * size * 4;
if (parsed.data.length !== expected) {
  throw new Error(`LUT data length ${parsed.data.length} != ${expected}`);
}

Try / catch

try {
  applyLut(state, content);
} catch (e) {
  if (e.message.startsWith('Failed to upload the 3D LUT texture')) {
    // decode WebGL error enum in message; on OUT_OF_MEMORY use a smaller LUT,
    // if gl.isContextLost() recreate the context and setupLut again
  }
}

Prevention

When it happens

Trigger: texImage3D fails with GL errors such as OUT_OF_MEMORY (LUT too large for GPU memory), INVALID_VALUE (malformed data length vs LUT_3D_SIZE^3), or driver-specific float-texture restrictions; also fires after context loss.

Common situations: Oversized LUTs on memory-constrained mobile GPUs; .cube files whose declared size doesn't match the number of data rows; browsers/GPUs with poor float (RGBA32F) texture support; context lost between texture creation and upload.

Related errors


AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18). Data as JSON: /api/errors/ba5b3482932876d6. Report an issue: GitHub.

Appendix: source

Thrown at packages/effects/src/lut/lut-runtime.ts:202

		gl.activeTexture(gl.TEXTURE1);
		gl.bindTexture(gl.TEXTURE_3D, lutTexture);
		gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
		gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
		gl.texImage3D(
			gl.TEXTURE_3D,
			0,
			gl.RGBA16F,
			parsed.size,
			parsed.size,
			parsed.size,
			0,
			gl.RGBA,
			gl.FLOAT,
			parsed.data,
		);
		const uploadError = gl.getError();
		if (uploadError !== gl.NO_ERROR) {
			throw new Error(
				`Failed to upload the 3D LUT texture (WebGL error ${uploadError})`,
			);
		}

		gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
		state.cachedContent = content;
	}

	gl.viewport(0, 0, width, height);
	gl.bindFramebuffer(gl.FRAMEBUFFER, null);
	gl.clearColor(0, 0, 0, 0);
	gl.clear(gl.COLOR_BUFFER_BIT);

	gl.activeTexture(gl.TEXTURE0);
	gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
	gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
	gl.texImage2D(
		gl.TEXTURE_2D,

View on GitHub (pinned to 46a3a6bf13)