{"record":{"id":"e331efe93c75be2a","repo":"mrdoob/three.js","slug":"three-batchedmesh-reserved-space-not-large-enough","errorCode":null,"errorMessage":"THREE.BatchedMesh: Reserved space not large enough for provided geometry.","messagePattern":"THREE\\.BatchedMesh: Reserved space not large enough for provided geometry\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/objects/BatchedMesh.js","lineNumber":735,"sourceCode":"\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\n\t\t}\n\n\t\t// copy geometry buffer data over\n\t\tconst vertexStart = geometryInfo.vertexStart;\n\t\tconst reservedVertexCount = geometryInfo.reservedVertexCount;\n\t\tgeometryInfo.vertexCount = geometry.getAttribute( 'position' ).count;\n\n\t\tfor ( const attributeName in batchGeometry.attributes ) {\n\n\t\t\t// copy attribute data\n\t\t\tconst srcAttribute = geometry.getAttribute( attributeName );\n\t\t\tconst dstAttribute = batchGeometry.getAttribute( attributeName );\n\t\t\tcopyAttributeData( srcAttribute, dstAttribute, vertexStart );\n\n\t\t\t// fill the rest in with zeroes\n\t\t\tconst itemSize = srcAttribute.itemSize;\n\t\t\tfor ( let i = srcAttribute.count, l = reservedVertexCount; i < l; i ++ ) {","sourceCodeStart":717,"sourceCodeEnd":753,"githubUrl":"https://github.com/mrdoob/three.js/blob/da05705fa31f02710c19772a2aa70c7454807f0c/src/objects/BatchedMesh.js#L717-L753","documentation":"Thrown by BatchedMesh.setGeometryAt() when the new geometry needs more vertices or indices than were reserved at addGeometry() time. Each geometry slot has a fixed reservedVertexCount/reservedIndexCount; setGeometryAt refuses to write past that reservation even if the backing buffer has spare room.","triggerScenarios":"addGeometry was called with a small reservedVertexCount/reservedIndexCount (or the default sized to a placeholder), then setGeometryAt replaces it with a geometry whose position.count or index.count exceeds the reservation. Replacing a low-LOD mesh with a high-LOD variant in the same slot.","commonSituations":"LOD swapping where the high-detail mesh is larger than the initial reservation; loading async geometry where the final size was unknown at reservation time and defaulted to -1 (the placeholder's size); procedural geometry that grew.","solutions":["When calling addGeometry, reserve enough space for the LARGEST geometry you will ever place in that slot: addGeometry(placeholder, maxVerts, maxIndices).","If the slot is too small, deleteGeometry then addGeometry again with a larger reservation to get a fresh ID.","Pre-measure all candidate geometries and pass the max as the reservation before any setGeometryAt.","Track per-slot reservation and validate geometry.attributes.position.count and geometry.index.count against it before calling setGeometryAt."],"exampleFix":"// before\nconst id = batch.addGeometry( lowLodGeo ); // reserved = lowLod size\nbatch.setGeometryAt( id, highLodGeo ); // throws: highLod > reserved\n\n// after\nconst maxVerts = Math.max( lowLodGeo.attributes.position.count, highLodGeo.attributes.position.count );\nconst maxIdx = Math.max( lowLodGeo.index.count, highLodGeo.index.count );\nconst id = batch.addGeometry( lowLodGeo, maxVerts, maxIdx );\nbatch.setGeometryAt( id, highLodGeo );","handlingStrategy":"validation","validationCode":"const info = batchedMesh.geometryInfo[ geometryId ];\nconst fitsV = geometry.attributes.position.count <= info.reservedVertexCount;\nconst idx = geometry.getIndex();\nconst fitsI = !idx || ( batchedMesh.geometry.index && idx.count <= info.reservedIndexCount );\nif ( fitsV && fitsI ) batchedMesh.setGeometryAt( geometryId, geometry );","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Reserve for the LARGEST geometry variant at addGeometry time.","Measure all candidate geometries up front and pass the max as the reservation.","If a slot is too small, deleteGeometry and re-add with a larger reservation.","Validate position.count and index.count against the slot reservation before setGeometryAt."],"tags":["batchedmesh","reservation","geometry","lod"],"backgroundTag":null,"analyzedSha":"da05705fa31f02710c19772a2aa70c7454807f0c","analyzedAt":"2026-08-12T22:51:37.160Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}