mrdoob/three.js · error · Error

THREE.Renderer: "getArrayBufferAsync()" offset and count mus

Error message

THREE.Renderer: "getArrayBufferAsync()" offset and count must be a multiple of 4.

What it means

Thrown by Renderer.getArrayBufferAsync() when the offset or a positive count is not a multiple of 4 (bytes). GPU readback via getBufferSubData/async copies requires 4-byte alignment for the offset and length to satisfy buffer copy constraints.

Source

Thrown at src/renderers/common/Renderer.js:2125

				this.info.createReadbackBuffer( target );

				const disposeInfo = () => {

					target.removeEventListener( 'dispose', disposeInfo );

					this.info.destroyReadbackBuffer( target );

				};

				target.addEventListener( 'dispose', disposeInfo );

			}

		}

		if ( offset % 4 !== 0 || ( count > 0 && count % 4 !== 0 ) ) {

			throw new Error( 'THREE.Renderer: "getArrayBufferAsync()" offset and count must be a multiple of 4.' );

		}

		return await this.backend.getArrayBufferAsync( attribute, target, offset, count );

	}

	/**
	 * Returns the rendering context.
	 *
	 * @return {GPUCanvasContext|WebGL2RenderingContext} The rendering context.
	 */
	getContext() {

		return this.backend.getContext();

	}

View on GitHub (pinned to da05705fa3)

Solutions

  1. Align offset to a multiple of 4 (Math.floor(offset/4)*4) and round count up/down to a multiple of 4.
  2. Read whole attributes (offset 0, count -1 default) when alignment is hard to guarantee.
  3. Compute offset and count in terms of 4-byte units and multiply back by 4 before passing.
  4. If sub-range readback is essential, pad your attribute layout so sub-ranges stay 4-aligned.

Example fix

// before
await renderer.getArrayBufferAsync( attribute, null, 6, 7 ); // offset 6 and count 7 not mult of 4

// after
const offset = Math.floor( 6 / 4 ) * 4; // 4
const count = Math.ceil( 7 / 4 ) * 4;   // 8
await renderer.getArrayBufferAsync( attribute, null, offset, count );
Defensive patterns

Strategy: validation

Validate before calling

function aligned( n ) { return Math.floor( n / 4 ) * 4; }
const safeOffset = aligned( offset );
const safeCount = count > 0 ? Math.ceil( count / 4 ) * 4 : count;
await renderer.getArrayBufferAsync( attribute, target, safeOffset, safeCount );

Prevention

When it happens

Trigger: Passing an odd offset, or a count not divisible by 4, to getArrayBufferAsync(attribute, target, offset, count). Note count <= 0 (e.g. the default -1) skips the count alignment check, so only positive counts are validated.

Common situations: Reading sub-ranges of tightly packed attribute arrays with element sizes that break 4-byte alignment; computing offset/count from element counts without accounting for byte stride; partial reads for picking/debugging.

Related errors


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