sschmid/Entitas · error · ContextStillHasRetainedEntitiesException

ContextStillHasRetainedEntitiesException(this…

Error message

ContextStillHasRetainedEntitiesException(this, _retainedEntities)

What it means

DestroyAllEntities destroys all entities and clears the set, then asserts no entities remain retained (some object still holds a Retain). If retained entities exist, recycling bookkeeping would be corrupted, so Entitas throws instead of leaking them silently.

Solutions

  1. Ensure all retainers Release entities (e.g. Unlink all EntityLinks) before DestroyAllEntities
  2. Clear/reset all entity indexes and pools before resetting the context
  3. Fix asymmetry where Retain is called without a matching Release

Example fix

// before
context.Reset(); // throws if EntityLinks still retain entities
// after
foreach (var link in Object.FindObjectsOfType<EntityLink>())
    link.Unlink();
context.Reset();
Defensive patterns

Strategy: validation

Validate before calling

foreach (var link in Object.FindObjectsOfType<EntityLink>())
    if (link.entity != null) link.Unlink();
context.DestroyAllEntities();

Try / catch

try { context.Reset(); }
catch (Exception e) { Debug.LogError("Retained entities remain: release all retainers before reset"); }

Prevention

When it happens

Trigger: Calling context.Reset() or context.DestroyAllEntities() while external systems (indexes, pools, EntityLink, custom retainers) still hold Retain on entities that were destroyed.

Common situations: Forgetting to Unlink EntityLink components before context teardown, custom entity indexes retaining entities, or editor/test teardown not releasing retained entities.

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

Appendix: source

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

            entity.OnDestroyEntity += _OnDestroyEntityDelegate;

            OnEntityCreated?.Invoke(this, entity);

            return entity;
        }

        /// Destroys all entities in the context.
        /// Throws an exception if there are still retained entities.
        public void DestroyAllEntities()
        {
            var entities = GetEntities();
            for (var i = 0; i < entities.Length; i++)
                entities[i].Destroy();

            _entities.Clear();

            if (_retainedEntities.Count != 0)
                throw new ContextStillHasRetainedEntitiesException(this, _retainedEntities);
        }

        /// Determines whether the context has the specified entity.
        public bool HasEntity(TEntity entity) => _entities.Contains(entity);

        /// Returns all entities which are currently in the context.
        public TEntity[] GetEntities()
        {
            return _entitiesCache ??= _entities.ToArray();
        }

        /// Returns all entities matching the specified matcher.
        public TEntity[] GetEntities(IMatcher<TEntity> matcher)
        {
            return GetGroup(matcher).GetEntities();
        }

        /// Returns a group for the specified matcher.

View on GitHub (pinned to 37547d1bd2)