mrdoob/three.js · error · Error

THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarge

Error message

THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.

What it means

Thrown by readRenderTargetPixelsAsync when the target texture's type is not GPU-readable for pixel readback. Readability is determined by capabilities.textureTypeReadable(type); types outside UnsignedByteType (and implementation-defined readable types) cannot be read. This commonly hits floating-point (HalfFloat/Float) targets when the EXT_color_buffer_float / read-on-float extension is unavailable.

Source

Thrown at src/renderers/WebGLRenderer.js:3223

					const texture = renderTarget.textures[ textureIndex ];
					const textureFormat = texture.format;
					const textureType = texture.type;

					// when using MRT, select the correct color buffer for the subsequent read command

					if ( renderTarget.textures.length > 1 ) _gl.readBuffer( _gl.COLOR_ATTACHMENT0 + textureIndex );

					const readableState = getReadableState( texture );

					if ( readableState.__formatReadable === false ) {

						throw new Error( 'THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.' );

					}

					if ( readableState.__typeReadable === false ) {

						throw new Error( 'THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.' );

					}

					const glBuffer = _gl.createBuffer();
					_gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer );
					_gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ );

					_gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), 0 );

					// reset the frame buffer to the currently set buffer before waiting
					const currFramebuffer = _currentRenderTarget !== null ? properties.get( _currentRenderTarget ).__webglFramebuffer : null;
					state.bindFramebuffer( _gl.FRAMEBUFFER, currFramebuffer );

					// check if the commands have finished every 8 ms
					const sync = _gl.fenceSync( _gl.SYNC_GPU_COMMANDS_COMPLETE, 0 );

					_gl.flush();

View on GitHub (pinned to da05705fa3)

Solutions

  1. Use UnsignedByteType for targets you read back via this method, or confirm the context supports float readback (renderer.capabilities) before using FloatType/HalfFloatType.
  2. Render float results into an RGBA8/UnsignedByte target (encoding the values) and read that back instead.
  3. Request a context that exposes EXT_color_buffer_float / OES_texture_float and verify before relying on float readback.
  4. Downgrade the readback target type to UnsignedByteType as a portable fallback.

Example fix

// before
const rt = new THREE.WebGLRenderTarget( w, h, { type: THREE.FloatType } );
await renderer.readRenderTargetPixelsAsync( rt, 0, 0, w, h, buf ); // throws without float read

// after
const rt = new THREE.WebGLRenderTarget( w, h ); // UnsignedByteType default
await renderer.readRenderTargetPixelsAsync( rt, 0, 0, w, h, buf );
Defensive patterns

Strategy: validation

Validate before calling

const readable = renderer.capabilities && renderer.capabilities.textureTypeReadable
  ? renderer.capabilities.textureTypeReadable( renderTarget.texture.type )
  : ( renderTarget.texture.type === THREE.UnsignedByteType );
if ( readable ) {
  await renderer.readRenderTargetPixelsAsync( renderTarget, 0, 0, w, h, buf );
}

Prevention

When it happens

Trigger: Creating a WebGLRenderTarget with texture.type = HalfFloatType or FloatType, then readRenderTargetPixelsAsync, on a context that lacks float-readback support.

Common situations: HDR/compute-style pipelines that render to float targets for readback; mobile/Safari contexts without float-read extensions; reading back picking/GPU-compute results stored in float buffers.

Related errors


AI-assisted analysis of mrdoob/three.js@da05705fa3 (2026-08-12). Data as JSON: /api/errors/8dc235bf6fc43974. Report an issue: GitHub.