sschmid/Entitas · error · EntityIsNotEnabledException

Cannot destroy !

Error message

Cannot destroy {this}!

What it means

Thrown by Entity.Destroy when the entity is already destroyed/disabled. Destroy dispatches OnDestroyEntity; Entitas forbids destroying an entity twice because the destroy process (retainer release, component teardown, pool return) must run exactly once.

Solutions

  1. Guard with if (entity.isEnabled) entity.Destroy();
  2. Ensure only one system owns the destroy decision for an entity class
  3. Unsubscribe listeners in OnDestroyEntity handlers so post-destroy callbacks cannot call Destroy again
  4. Use context.DestroyAllEntities() only once during teardown

Example fix

// before
foreach (var e in _buffer) e.Destroy(); // may double-destroy
// after
foreach (var e in _buffer) {
    if (e.isEnabled) {
        e.Destroy();
    }
}
Defensive patterns

Strategy: validation

Validate before calling

if (entity.isEnabled) entity.Destroy();

Type guard

bool CanDestroy(Entity e) => e.isEnabled;

Try / catch

try { entity.Destroy(); }
catch (EntityIsNotEnabledException) { /* already destroyed */ }

Prevention

When it happens

Trigger: Calling entity.Destroy() on an entity already destroyed; destroying in two systems in the same tick; destroying from a listener that itself was triggered by the destroy process; double cleanup in teardown code.

Common situations: Multiple reactive systems matching the same entity and each destroying it; application shutdown paths that destroy entities unconditionally; listener callbacks not unsubscribed before destroy.

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/a7daba4332b86c5b. Report an issue: GitHub.

Appendix: source

Thrown at src/Entitas/Entity/Entity.cs:367

        /// Releases the entity. An owner can only release an entity
        /// if it retains it.
        /// Retain/Release is part of AERC (Automatic Entity Reference Counting)
        /// and is used internally to prevent pooling retained entities.
        /// If you use retain manually you also have to
        /// release it manually at some point.
        public void Release(object owner)
        {
            _aerc.Release(owner);
            if (_aerc.RetainCount == 0)
                OnEntityReleased?.Invoke(this);
        }

        // Dispatches OnDestroyEntity which will start the destroy process.
        public void Destroy()
        {
            if (!_isEnabled)
                throw new EntityIsNotEnabledException($"Cannot destroy {this}!");

            OnDestroyEntity?.Invoke(this);
        }

        // This method is used internally. Don't call it yourself.
        // Use entity.Destroy();
        public void InternalDestroy()
        {
            _isEnabled = false;
            RemoveAllComponents();
            OnComponentAdded = null;
            OnComponentReplaced = null;
            OnComponentRemoved = null;
            OnDestroyEntity = null;
        }

        // Do not call this method manually. This method is called by the context.
        public void RemoveAllOnEntityReleasedHandlers() => OnEntityReleased = null;

View on GitHub (pinned to 37547d1bd2)