Install
$ agentstack add skill-impertio-studio-cesiumjs-claude-skill-package-cesium-errors-memory ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Cesium Errors : Memory
Overview
WebGL resources held by CesiumJS objects are NOT garbage collected. A texture, vertex buffer, or shader stays on the GPU until something calls destroy() on the object that owns it. Dropping the last JavaScript reference frees nothing.
Every CesiumJS memory bug reduces to one of two causes:
- A
destroy()call that never happened. This is a real leak. - A reference still held when you believe it was released. This is a
lost-reference bug that only imitates a leak.
This skill separates the two and gives a deterministic teardown procedure.
When to Use
- Heap or GPU memory climbs on every load, navigation, or add/remove cycle.
- The tab slows down, freezes, or crashes after the app runs a while.
- Console shows
WARNING: Too many active WebGL contexts. Oldest context will be lost. - Console throws
DeveloperError: This object was destroyed and should not be used. - An older map view goes blank while a newer one still renders.
- A Chrome heap snapshot diff shows CesiumJS objects surviving teardown.
The Iron Rule
- ALWAYS call
destroy()on everyViewer,Cesium3DTileset,Model, custom
Primitive, custom PrimitiveCollection, and ScreenSpaceEventHandler you create, before you drop the reference.
- ALWAYS verify a teardown by checking
isDestroyed()returnstrue. - NEVER call any method except
isDestroyed()on an object afterdestroy().
Doing so throws DeveloperError.
- NEVER construct a new
Vieweron a code path that runs more than once.
Create one Viewer and reuse it.
- NEVER assume removing a primitive frees GPU memory unless the collection's
destroyPrimitives is true (see E3).
Ownership and Teardown : Quick Reference
| Object | Freed by | Notes | |--------|----------|-------| | Viewer | viewer.destroy() | Cascades to Scene, globe, scene.primitives, owned imagery layers and data sources. | | Cesium3DTileset or Model in viewer.scene.primitives | viewer.destroy() or scene.primitives.remove(obj) | scene.primitives.destroyPrimitives defaults true, so removal destroys the object. | | A primitive you removed and kept | Already destroyed by remove() | After removal from a destroyPrimitives: true collection it is destroyed. Do NOT reuse it. | | Custom new PrimitiveCollection({ destroyPrimitives: false }) | You destroy each member, then the collection | false disables automatic destruction. | | Entity in viewer.entities | viewer.entities.remove() or removeAll() | EntityCollection has NO destroy(). Visualizers free GPU primitives on the next render. | | DataSource in viewer.dataSources | viewer.dataSources.remove(ds, true) | The destroy parameter defaults false. Pass true to free it. | | ScreenSpaceEventHandler | handler.destroy() | The viewer does NOT own it. Always destroy it yourself. |
Decision Tree : real leak or lost reference?
digraph leak_triage {
rankdir=TB;
grow [shape=box, label="Memory grows over cycles"];
destroyed [shape=diamond, label="destroy() called on every created object?"];
verified [shape=diamond, label="isDestroyed() returns true after teardown?"];
real [shape=box, label="Real leak : add the missing destroy()"];
lost [shape=box, label="Lost reference : find the retainer (E5)"];
grow -> destroyed;
destroyed -> real [label="no"];
destroyed -> verified [label="yes"];
verified -> real [label="no"];
verified -> lost [label="yes"];
}
Error Catalog
E1 : WebGL resources leak after a dropped reference
- Symptom : GPU and heap memory climb every time a tileset, model, or
primitive loads. The tab eventually slows or throws CONTEXT_LOST_WEBGL.
- Root cause : WebGL buffers, textures, and shaders are not garbage
collected. Setting the variable to null or letting it leave scope frees nothing. Only destroy() releases GPU memory.
- Prevention : ALWAYS pair object creation with a
destroy()in the
matching teardown path. Objects inside viewer.scene.primitives are covered by a single viewer.destroy(). Destroy side-channel objects explicitly.
- Recovery : list every object created since startup; for each, confirm a
destroy() runs on teardown; add the missing call; assert isDestroyed().
E2 : DeveloperError "This object was destroyed"
- Symptom : `DeveloperError: This object was destroyed and should not be
used.` thrown when a method runs on a tileset, primitive, viewer, or handler.
- Root cause : the object was already destroyed, then reused. Common
trigger : scene.primitives.remove(primitive) destroys the primitive because PrimitiveCollection.destroyPrimitives defaults to true; stale code keeps the reference. The same happens after viewer.destroy().
- Prevention : after
remove()ordestroy(), set the reference tonull
immediately. Guard any reuse with if (!obj.isDestroyed()).
- Recovery : stop reusing the destroyed instance; recreate it through its
async factory (Cesium3DTileset.fromUrl, Model.fromGltfAsync).
E3 : Silent leak with destroyPrimitives set to false
- Symptom : heap grows across add/remove cycles even though
remove()or
removeAll() is called every time.
- Root cause : a
PrimitiveCollectionconstructed with
{ destroyPrimitives: false } does NOT destroy its members on remove, removeAll, or destroy. The caller owns destruction and skipped it.
- Prevention : keep
destroyPrimitivesat its defaulttrueunless
primitives are deliberately shared between collections. When it must be false, call destroy() on each primitive yourself after removing it.
- Recovery : track members in an array; on clear,
destroy()each one,
then removeAll().
E4 : Memory retained after entity removal
- Symptom : after
viewer.entities.removeAll(), CPU memory stays high;
with very large entity counts the app stutters every tick.
- Root cause :
EntityCollectionhas nodestroy()method. Entity
visualizers free the underlying GPU primitives lazily on the next render pass, and the per-tick visualizer loop scales with entity count. Historically about 100 MB per 5000 entities (issues #6534, #8767).
- Prevention : cap entity count; above roughly 10000 features migrate to
Cesium3DTileset or batched Primitive plus GeometryInstance; reuse entities instead of churning remove and add.
- Recovery : call
removeAll(), then allow one render pass so visualizers
release primitives; for a hard reset, viewer.destroy() and create one fresh viewer.
E5 : Heap grows but it is not a CesiumJS leak
- Symptom : a heap snapshot diff shows CesiumJS objects surviving an
add/remove cycle even though destroy() was called.
- Root cause : CesiumJS pools and reuses memory, so modest steady-state
growth is expected. A genuinely retained object is almost always held by application code : a closure, a module-level array, an un-removed event listener (clock.onTick, scene.preRender, camera.changed), a requestAnimationFrame callback, or framework state. XHR responses can also be retained by a pending Request (issue #8843).
- Prevention : store each CesiumJS object in exactly one owner; for every
addEventListener on a CesiumJS Event, keep the returned remover function and call it on teardown.
- Recovery : take two Chrome heap snapshots around one cycle, diff
retainers, and follow the retaining path to the application code holding the reference; remove that path. See references/examples.md.
E6 : Too many active WebGL contexts
- Symptom : console warning `WARNING: Too many active WebGL contexts.
Oldest context will be lost.; an older Cesium view goes blank or throws CONTEXTLOSTWEBGL`.
- Root cause : a browser tab allows only about 16 live WebGL contexts. Each
Viewer and each CesiumWidget holds one. Single-page-app route changes, React component re-mounts (StrictMode mounts twice in development), and modal open/close cycles create new viewers faster than old ones are destroyed. viewer.destroy() does not always release the context immediately (issue #11533).
- Prevention : create exactly ONE
Viewerand reuse it across routes and
components; ALWAYS call viewer.destroy() in the unmount or cleanup path; never construct a Viewer inside a function that runs on every render.
- Recovery : destroy every stray viewer; consolidate to a single long-lived
viewer; in React, hold the viewer in a useRef and create it once.
Teardown Ordering
viewer.destroy() cascades to its Scene, the globe, scene.primitives (which destroys contained primitives because its destroyPrimitives is true), and the imagery layers and data sources the viewer owns. Side-channel objects are NOT covered. Tear down in this order:
- Remove every CesiumJS event listener you added (
clock.onTick,
scene.preRender, camera.changed) by calling the remover that each addEventListener returned.
- Call
destroy()on everyScreenSpaceEventHandleryou created. - Call
destroy()on any customPrimitiveCollectionand on primitives it
held with destroyPrimitives: false.
- Call
viewer.destroy()LAST. - Set the viewer reference and all child references to
null.
NEVER read viewer, viewer.scene, or any child after step 4. A complete React useEffect cleanup is in references/examples.md.
Tileset Memory Budget
A Cesium3DTileset caps its GPU cache with cacheBytes (default 536870912, 512 MiB) plus maximumCacheOverflowBytes (default 536870912). Read tileset.totalMemoryUsageInBytes to measure live usage. The removed maximumMemoryUsage property no longer exists; code that sets it silently does nothing. Lower cacheBytes to cap peak GPU memory at the cost of more tile refetching. Signatures are in references/methods.md.
Common Mistakes
| Mistake | Consequence | Fix | |---------|-------------|-----| | Setting a tileset variable to null without destroy() | GPU memory leaks | Remove it from scene.primitives, or call tileset.destroy() first | | Reusing a primitive after scene.primitives.remove() | DeveloperError | Null the reference; recreate via the async factory | | new PrimitiveCollection({ destroyPrimitives: false }) then removeAll() | Silent leak | Destroy each member, or keep destroyPrimitives true | | viewer.dataSources.remove(ds) without the destroy flag | DataSource leaks | Use viewer.dataSources.remove(ds, true) | | New Viewer per React mount | 16-context budget exhausted | One viewer in a useRef, destroyed on unmount | | Reading viewer.scene after viewer.destroy() | DeveloperError | Null references; never touch after destroy |
Red Flags : STOP
- A variable holding a CesiumJS object is set to
nulland nodestroy()ran first. - A
Vieweris constructed insiderender, auseEffectwithout cleanup, or a loop. destroyPrimitives: falseappears without a matching manualdestroy()loop.- An
addEventListeneron a CesiumJSEventhas no paired remover on teardown. - Code reads a property of an object after that object was removed or destroyed.
Each of these means a leak or a DeveloperError is already present. Fix before shipping.
Reference Files
references/methods.md: verified signatures and semantics ofdestroy(),
isDestroyed(), PrimitiveCollection.destroyPrimitives, DataSourceCollection.remove, and the Cesium3DTileset memory budget.
references/examples.md: a complete teardown function, a ReactuseEffect
cleanup, tileset memory-budget configuration, and a heap-snapshot workflow.
references/anti-patterns.md: each memory anti-pattern with symptom, root
cause, prevention, and recovery, traced to CesiumGS GitHub issues.
Sources
Verified via WebFetch on 2026-05-20 against the CesiumJS API Reference (https://cesium.com/learn/cesiumjs/ref-doc/) : Viewer, Scene, Cesium3DTileset, PrimitiveCollection, DataSourceCollection, EntityCollection, ScreenSpaceEventHandler.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Impertio-Studio
- Source: Impertio-Studio/CesiumJS-Claude-Skill-Package
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.