{"record":{"id":"3926e7d238fb0126","repo":"mrdoob/three.js","slug":"three-batchedmesh-maximum-geometry-count-reached","errorCode":null,"errorMessage":"THREE.BatchedMesh: Maximum geometry count reached.","messagePattern":"THREE\\.BatchedMesh: Maximum geometry count reached\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/objects/BatchedMesh.js","lineNumber":718,"sourceCode":"\n\t\treturn geometryId;\n\n\t}\n\n\t/**\n\t * Replaces the geometry at the given ID with the provided geometry. Throws an error if there\n\t * is not enough space reserved for geometry. Calling this will change all instances that are\n\t * rendering that geometry.\n\t *\n\t * @param {number} geometryId - The ID of the geometry that should be replaced with the given geometry.\n\t * @param {BufferGeometry} geometry - The new geometry.\n\t * @return {number} The geometry ID.\n\t */\n\tsetGeometryAt( geometryId, geometry ) {\n\n\t\tif ( geometryId >= this._geometryCount ) {\n\n\t\t\tthrow new Error( 'THREE.BatchedMesh: Maximum geometry count reached.' );\n\n\t\t}\n\n\t\tthis._validateGeometry( geometry );\n\n\t\tconst batchGeometry = this.geometry;\n\t\tconst hasIndex = batchGeometry.getIndex() !== null;\n\t\tconst dstIndex = batchGeometry.getIndex();\n\t\tconst srcIndex = geometry.getIndex();\n\t\tconst geometryInfo = this._geometryInfo[ geometryId ];\n\t\tif (\n\t\t\thasIndex &&\n\t\t\tsrcIndex.count > geometryInfo.reservedIndexCount ||\n\t\t\tgeometry.attributes.position.count > geometryInfo.reservedVertexCount\n\t\t) {\n\n\t\t\tthrow new Error( 'THREE.BatchedMesh: Reserved space not large enough for provided geometry.' );\n","sourceCodeStart":700,"sourceCodeEnd":736,"githubUrl":"https://github.com/mrdoob/three.js/blob/da05705fa31f02710c19772a2aa70c7454807f0c/src/objects/BatchedMesh.js#L700-L736","documentation":"Thrown by BatchedMesh.setGeometryAt() when the supplied geometryId is greater than or equal to the current _geometryCount. Geometry IDs are only valid in the range [0, _geometryCount), so passing an out-of-range, stale, or pre-allocation ID is treated as programmer error rather than silently ignored.","triggerScenarios":"Calling batchedMesh.setGeometryAt(geometryId, geometry) with a geometryId that was never returned by addGeometry; using an ID after deleteGeometry freed and the count shrank; passing a raw integer constant instead of the stored ID; iterating IDs up to the constructor's maxGeometryCount rather than the live geometryCount.","commonSituations":"Hardcoding geometry IDs instead of storing addGeometry's return value; holding IDs across a deleteGeometry/compaction; off-by-one loops over geometry slots; confusing maxGeometryCount (capacity) with the live geometryCount.","solutions":["Always store and reuse the ID returned by addGeometry instead of inventing one.","Guard the call: if (geometryId >= batchedMesh.geometryCount) return; before setGeometryAt.","After deleteGeometry, discard any IDs you held for freed slots or re-map them.","Use batchedMesh.geometryCount (the live count) as the loop bound, not the constructor capacity."],"exampleFix":"// before\nbatch.setGeometryAt( 0, newGeom ); // 0 may be invalid if geometry was deleted\n\n// after\nconst id = batch.addGeometry( placeholderGeo );\nbatch.setGeometryAt( id, newGeom );","handlingStrategy":"validation","validationCode":"if ( geometryId < batchedMesh.geometryCount ) {\n  batchedMesh.setGeometryAt( geometryId, geometry );\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always store and reuse the ID returned by addGeometry; never invent IDs.","Use batchedMesh.geometryCount (live count) as loop bounds, not the constructor capacity.","After deleteGeometry, discard any held IDs for freed slots.","Validate geometryId against geometryCount before setGeometryAt."],"tags":["batchedmesh","invalid-id","api-contract"],"backgroundTag":null,"analyzedSha":"da05705fa31f02710c19772a2aa70c7454807f0c","analyzedAt":"2026-08-12T22:51:37.160Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}