stride3d/stride · error · InvalidOperationException

Trying to remove unregistered collider

Error message

Trying to remove unregistered collider

What it means

NavigationMeshBuilder.Remove unregisters a collider by its component Id. Throwing InvalidOperationException when the Id is not registered catches lifecycle bugs where removal happens for a collider that was never added or was already removed.

Solutions

  1. Check that the collider was registered with this builder before removing (track registration yourself or expose the registered set)
  2. Make add/remove lifecycle symmetric: remove only in response to the matching removal of a previously added component
  3. Swallow the case intentionally by checking membership first if double-removal is expected in your teardown flow

Example fix

// before
builder.Remove(colliderData); // may be unregistered
// after
if (registered.Contains(colliderData.Component.Id))
    builder.Remove(colliderData);
Defensive patterns

Strategy: validation

Validate before calling

// only remove what you added
if (addedColliders.Remove(colliderData))
    builder.Remove(colliderData);

Type guard

null

Try / catch

try
{
    builder.Remove(colliderData);
}
catch (InvalidOperationException)
{
    // not registered; ignore during teardown
}

Prevention

When it happens

Trigger: Calling Remove(StaticColliderData) for a component that was never passed to Add, calling Remove twice for the same component, or removing after the builder state was reset.

Common situations: Asymmetric add/remove lifecycle in scene teardown; removing a collider from a different NavigationMeshBuilder instance than the one that added it; scripts running cleanup on scene unload twice.

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 stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/596bf24f0b65d247. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Navigation/NavigationMeshBuilder.cs:72

            lock (colliders)
            {
                if (registeredGuids.Contains(colliderData.Component.Id))
                    throw new InvalidOperationException("Duplicate collider added");
                colliders.Add(colliderData);
                registeredGuids.Add(colliderData.Component.Id);
            }
        }

        /// <summary>
        /// Removes a specific collider from the builder
        /// </summary>
        /// <param name="colliderData">The collider data object to remove</param>
        public void Remove(StaticColliderData colliderData)
        {
            lock (colliders)
            {
                if (!registeredGuids.Contains(colliderData.Component.Id))
                    throw new InvalidOperationException("Trying to remove unregistered collider");
                colliders.Remove(colliderData);
                registeredGuids.Remove(colliderData.Component.Id);
            }
        }

        /// <summary>
        /// Performs the build of a navigation mesh
        /// </summary>
        /// <param name="buildSettings">The build settings to pass to recast</param>
        /// <param name="groups">A collection of agent settings to use, this will generate a layer in the navigation mesh for every agent settings in this collection (in the same order)</param>
        /// <param name="includedCollisionGroups">The collision groups that will affect which colliders are considered solid</param>
        /// <param name="boundingBoxes">A collection of bounding boxes to use as the region for which to generate navigation mesh tiles</param>
        /// <param name="cancellationToken">A cancellation token to interrupt the build process</param>
        /// <returns>The build result</returns>
        public NavigationMeshBuildResult Build(NavigationMeshBuildSettings buildSettings, ICollection<NavigationMeshGroup> groups, CollisionFilterGroupFlags includedCollisionGroups,
            ICollection<BoundingBox> boundingBoxes, CancellationToken cancellationToken)
        {
            var lastCache = oldNavigationMesh?.Cache;

View on GitHub (pinned to 96fad776d2)