{"record":{"id":"4a5750e57b4a9f04","repo":"mrdoob/three.js","slug":"three-renderer-getarraybufferasync-offset-and","errorCode":null,"errorMessage":"THREE.Renderer: \"getArrayBufferAsync()\" offset and count must be a multiple of 4.","messagePattern":"THREE\\.Renderer: \"getArrayBufferAsync\\(\\)\" offset and count must be a multiple of 4\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/renderers/common/Renderer.js","lineNumber":2125,"sourceCode":"\t\t\t\tthis.info.createReadbackBuffer( target );\n\n\t\t\t\tconst disposeInfo = () => {\n\n\t\t\t\t\ttarget.removeEventListener( 'dispose', disposeInfo );\n\n\t\t\t\t\tthis.info.destroyReadbackBuffer( target );\n\n\t\t\t\t};\n\n\t\t\t\ttarget.addEventListener( 'dispose', disposeInfo );\n\n\t\t\t}\n\n\t\t}\n\n\t\tif ( offset % 4 !== 0 || ( count > 0 && count % 4 !== 0 ) ) {\n\n\t\t\tthrow new Error( 'THREE.Renderer: \"getArrayBufferAsync()\" offset and count must be a multiple of 4.' );\n\n\t\t}\n\n\t\treturn await this.backend.getArrayBufferAsync( attribute, target, offset, count );\n\n\t}\n\n\t/**\n\t * Returns the rendering context.\n\t *\n\t * @return {GPUCanvasContext|WebGL2RenderingContext} The rendering context.\n\t */\n\tgetContext() {\n\n\t\treturn this.backend.getContext();\n\n\t}\n","sourceCodeStart":2107,"sourceCodeEnd":2143,"githubUrl":"https://github.com/mrdoob/three.js/blob/da05705fa31f02710c19772a2aa70c7454807f0c/src/renderers/common/Renderer.js#L2107-L2143","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Align offset to a multiple of 4 (Math.floor(offset/4)*4) and round count up/down to a multiple of 4.","Read whole attributes (offset 0, count -1 default) when alignment is hard to guarantee.","Compute offset and count in terms of 4-byte units and multiply back by 4 before passing.","If sub-range readback is essential, pad your attribute layout so sub-ranges stay 4-aligned."],"exampleFix":"// before\nawait renderer.getArrayBufferAsync( attribute, null, 6, 7 ); // offset 6 and count 7 not mult of 4\n\n// after\nconst offset = Math.floor( 6 / 4 ) * 4; // 4\nconst count = Math.ceil( 7 / 4 ) * 4;   // 8\nawait renderer.getArrayBufferAsync( attribute, null, offset, count );","handlingStrategy":"validation","validationCode":"function aligned( n ) { return Math.floor( n / 4 ) * 4; }\nconst safeOffset = aligned( offset );\nconst safeCount = count > 0 ? Math.ceil( count / 4 ) * 4 : count;\nawait renderer.getArrayBufferAsync( attribute, target, safeOffset, safeCount );","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Round offset down and count up/down to a multiple of 4.","Prefer whole-attribute reads (offset 0, count -1) when possible.","Compute offset/count in 4-byte units and scale back by 4.","Pad attribute layouts so sub-range reads stay 4-aligned."],"tags":["renderer","readback","alignment","buffer","webgpu"],"backgroundTag":null,"analyzedSha":"da05705fa31f02710c19772a2aa70c7454807f0c","analyzedAt":"2026-08-12T22:51:37.160Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}