Batch.updateShows crash: TypeError: Cannot read properties of undefined (reading 'id') when toggling show + removing ground-clamped entity in same frame
Summary
Toggling entity.show on a ground-clamped polyline (or polygon) and then removing it (or changing its material) in the same frame crashes the render loop with:
TypeError: Cannot read properties of undefined (reading 'id')
at Batch.updateShows (StaticGroundPolylinePerMaterialBatch.js)
at Batch.update
at GeometryVisualizer.update
at DataSourceDisplay.update
at CesiumWidget.render
Sandcastle reproduction: link to sandcastle
Steps to reproduce
- Create a ground-clamped polyline entity (
clampToGround: true) - Wait for the ground primitive to compile
- In the same synchronous block:
- Set
entity.show = false - Call
viewer.entities.remove(entity)
- Set
- The next render frame crashes
Root cause
Batch.prototype.remove() in StaticGroundPolylinePerMaterialBatch.js and StaticGroundGeometryPerMaterialBatch.js cleans up geometry, updaters, subscriptions — but does not call this.showsUpdated.remove(id).
When entity.show is toggled, the batch’s definitionChanged listener synchronously adds the updater to showsUpdated. If the entity is then removed before the next render, the sequence during GeometryVisualizer.update() is:
- Process removals →
Batch.remove(updater)→geometry.remove(id)✓, butshowsUpdatedstill has the entry - Update batches →
Batch.updateShows()→ iteratesshowsUpdated→geometry.get(updater.id)returnsundefined→instance.idcrashes
The same issue occurs when an entity’s material changes (triggering batch migration) — the old batch retains the stale showsUpdated entry.
Affected files
@cesium/engine/Source/DataSources/StaticGroundPolylinePerMaterialBatch.js@cesium/engine/Source/DataSources/StaticGroundGeometryPerMaterialBatch.js
Other batch files (StaticGeometryColorBatch, StaticGeometryPerMaterialBatch, StaticGroundGeometryColorBatch, StaticOutlineGeometryBatch) have the same potential issue in updateShows — they lack a null guard on instance after geometry.get().
Suggested fix
1. Add cleanup in Batch.prototype.remove() (primary fix):
Batch.prototype.remove = function (updater) {
const id = updater.id;
this.createPrimitive = this.geometry.remove(id) || this.createPrimitive;
if (this.updaters.remove(id)) {
this.updatersWithAttributes.remove(id);
const unsubscribe = this.subscriptions.get(id);
if (defined(unsubscribe)) {
unsubscribe();
this.subscriptions.remove(id);
+ this.showsUpdated.remove(id); // clean up stale entry
}
return true;
}
return false;
};
2. Add defensive null check in Batch.prototype.updateShows() (belt-and-suspenders):
Batch.prototype.updateShows = function (primitive) {
const showsUpdated = this.showsUpdated.values;
const length = showsUpdated.length;
for (let i = 0; i < length; i++) {
const updater = showsUpdated[i];
const instance = this.geometry.get(updater.id);
+ if (!defined(instance)) {
+ continue;
+ }
// ... rest of method
}
this.showsUpdated.removeAll();
};
Environment
- Cesium version: 1.120.0 (bug appears to be present in many versions)
- Browser: Chrome 134
- OS: macOS / Windows