sschmid/Entitas · error · ContextInfoException

ContextInfoException(this, contextInfo)

Error message

ContextInfoException(this, contextInfo)

What it means

When a custom ContextInfo (with component names) is passed to the Context constructor, its ComponentNames array length must exactly equal totalComponents. Entitas validates this because component names are indexed by component index; a mismatch would produce wrong or missing names.

Solutions

  1. Ensure ComponentNames.Length equals the totalComponents argument
  2. Regenerate or update ContextInfo after changing the component list
  3. Let contextInfo be null (auto-generates "Index N" names) if you don't need custom names

Example fix

// before
new Context<Entity>(3, 0, new ContextInfo("Game", new[] { "A", "B" }, types)); // 2 names vs 3 components
// after
new Context<Entity>(3, 0, new ContextInfo("Game", new[] { "A", "B", "C" }, types));
Defensive patterns

Strategy: validation

Validate before calling

if (contextInfo != null && contextInfo.ComponentNames.Length != totalComponents)
    throw new ArgumentException("ComponentNames length must equal totalComponents");

Prevention

When it happens

Trigger: new Context<MyEntity>(numComponents, 0, new ContextInfo("Game", new[] { "Position", "Velocity" }, componentTypes)) where ComponentNames.Length != totalComponents.

Common situations: Manually maintained ContextInfo not updated after adding/removing a component class, code-gen config changes, or reusing one ContextInfo across contexts with different totalComponents counts.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

        readonly EntityEvent _OnEntityReleasedDelegate;
        readonly EntityEvent _OnDestroyEntityDelegate;

        /// The preferred way to create a context is to use the generated methods
        /// from the code generator, e.g. var context = new MainContext();
        public Context(int totalComponents, Func<TEntity> entityFactory) : this(totalComponents, 0, null, null, entityFactory) { }

        /// The preferred way to create a context is to use the generated methods
        /// from the code generator, e.g. var context = new GameContext();
        public Context(int totalComponents, int startCreationIndex, ContextInfo contextInfo, Func<Entity, IAERC> aercFactory, Func<TEntity> entityFactory)
        {
            _totalComponents = totalComponents;
            _creationIndex = startCreationIndex;

            if (contextInfo != null)
            {
                _contextInfo = contextInfo;
                if (contextInfo.ComponentNames.Length != totalComponents)
                    throw new ContextInfoException(this, contextInfo);
            }
            else
            {
                var componentNames = new string[_totalComponents];
                for (var i = 0; i < componentNames.Length; i++)
                    componentNames[i] = "Index " + i;

                _contextInfo = new ContextInfo("Unnamed Context", componentNames, null);
            }

            _aercFactory = aercFactory ?? SafeAERC.Delegate;
            _entityFactory = entityFactory;

            _groupsForIndex = new List<IGroup<TEntity>>[totalComponents];
            _componentPools = new Stack<IComponent>[totalComponents];
            _entityIndexes = new Dictionary<string, IEntityIndex>();

            var groupChangedListPool = new Stack<List<GroupChanged<TEntity>>>();

View on GitHub (pinned to 37547d1bd2)