mrdoob/three.js · error · Error

THREE.BatchedMesh: Maximum geometry count reached.

Error message

THREE.BatchedMesh: Maximum geometry count reached.

What it means

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.

Source

Thrown at src/objects/BatchedMesh.js:718

		return geometryId;

	}

	/**
	 * Replaces the geometry at the given ID with the provided geometry. Throws an error if there
	 * is not enough space reserved for geometry. Calling this will change all instances that are
	 * rendering that geometry.
	 *
	 * @param {number} geometryId - The ID of the geometry that should be replaced with the given geometry.
	 * @param {BufferGeometry} geometry - The new geometry.
	 * @return {number} The geometry ID.
	 */
	setGeometryAt( geometryId, geometry ) {

		if ( geometryId >= this._geometryCount ) {

			throw new Error( 'THREE.BatchedMesh: Maximum geometry count reached.' );

		}

		this._validateGeometry( geometry );

		const batchGeometry = this.geometry;
		const hasIndex = batchGeometry.getIndex() !== null;
		const dstIndex = batchGeometry.getIndex();
		const srcIndex = geometry.getIndex();
		const geometryInfo = this._geometryInfo[ geometryId ];
		if (
			hasIndex &&
			srcIndex.count > geometryInfo.reservedIndexCount ||
			geometry.attributes.position.count > geometryInfo.reservedVertexCount
		) {

			throw new Error( 'THREE.BatchedMesh: Reserved space not large enough for provided geometry.' );

View on GitHub (pinned to da05705fa3)

Solutions

  1. Always store and reuse the ID returned by addGeometry instead of inventing one.
  2. Guard the call: if (geometryId >= batchedMesh.geometryCount) return; before setGeometryAt.
  3. After deleteGeometry, discard any IDs you held for freed slots or re-map them.
  4. Use batchedMesh.geometryCount (the live count) as the loop bound, not the constructor capacity.

Example fix

// before
batch.setGeometryAt( 0, newGeom ); // 0 may be invalid if geometry was deleted

// after
const id = batch.addGeometry( placeholderGeo );
batch.setGeometryAt( id, newGeom );
Defensive patterns

Strategy: validation

Validate before calling

if ( geometryId < batchedMesh.geometryCount ) {
  batchedMesh.setGeometryAt( geometryId, geometry );
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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