sschmid/Entitas · error · EntityIsNotDestroyedException

Cannot release !

Error message

Cannot release {entity}!

What it means

Context's internal OnEntityReleased handler throws this when an entity is released (its retain count dropping) while it is still enabled, i.e. it was not destroyed first. In Entitas an entity must be destroyed (removed from the context) before its last retain is released so it can be recycled.

Solutions

  1. Always call entity.Destroy() before the last entity.Release(this)
  2. Balance every Retain with exactly one Release on entity destruction
  3. Audit custom retaining code (indexes, pools) for missed Release calls
  4. Check entity.IsEnabled / entity.retainCount before releasing

Example fix

// before
entity.Release(this); // throws when entity still enabled
// after
if (entity.IsEnabled) entity.Destroy();
entity.Release(this);
Defensive patterns

Strategy: type-guard

Validate before calling

if (entity.IsEnabled) entity.Destroy();
entity.Release(this);

Type guard

bool CanRelease(IEntity e) => !e.IsEnabled;

Prevention

When it happens

Trigger: An object that Retain()ed an entity calls Release() after the entity has been recycled by Destroy() but is again enabled, or releasing a retained entity that was never destroyed while enabled — the guard fires when entity.IsEnabled is true at release time.

Common situations: Custom Retain/Release usage (e.g. EntityLink-like systems), asymmetric retain/release in pools, or releasing a live entity obtained from context.CreateEntity().

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of sschmid/Entitas@37547d1bd2 (2026-09-14). Data as JSON: /api/errors/49e7096ce2260ca9. Report an issue: GitHub.

Appendix: source

Thrown at src/Entitas/Context/Context.cs:144

                        events[i]?.Invoke(groups[i], tEntity, index, component);

                    events.Clear();
                    groupChangedListPool.Push(events);
                }
            };

            _onComponentReplacedDelegate = (entity, index, previousComponent, newComponent) =>
            {
                var groups = _groupsForIndex[index];
                if (groups != null)
                    for (var i = 0; i < groups.Count; i++)
                        groups[i].UpdateEntity((TEntity)entity, index, previousComponent, newComponent);
            };

            _OnEntityReleasedDelegate = entity =>
            {
                if (entity.IsEnabled)
                    throw new EntityIsNotDestroyedException($"Cannot release {entity}!");

                var tEntity = (TEntity)entity;
                entity.RemoveAllOnEntityReleasedHandlers();
                _retainedEntities.Remove(tEntity);
                _reusableEntities.Push(tEntity);
            };

            _OnDestroyEntityDelegate = entity =>
            {
                var tEntity = (TEntity)entity;
                var removed = _entities.Remove(tEntity);
                if (!removed)
                    throw new ContextDoesNotContainEntityException(
                        $"'{this}' cannot destroy {tEntity}!",
                        "This cannot happen!?!"
                    );

                _entitiesCache = null;

View on GitHub (pinned to 37547d1bd2)