stride3d/stride · error · InvalidOperationException

does not consist of triangles

Error message

{shapeType} does not consist of triangles

What it means

During navmesh input building, a ConvexHullColliderShape's index buffer must be a triangle list (count divisible by 3). Throwing InvalidOperationException detects a corrupt or malformed hull index buffer before it would corrupt the built navmesh geometry.

Solutions

  1. Regenerate the convex hull asset with the compatible V-HACD pipeline so Indices.Count is a multiple of 3
  2. Validate/repair the offending collider's hull data (check Indices.Count % 3 == 0) before building the navmesh
  3. Remove or replace the malformed convex hull collider (e.g. use a box/collider primitive) to unblock the build

Example fix

// before
var hull = ColliderShape as ConvexHullColliderShape; // malformed Indices
// after
if (hull != null && hull.Indices.Count % 3 != 0)
{
    // regenerate or fix the convex hull asset before building
    throw new InvalidOperationException($"Convex hull on {entity.Name} has non-triangle indices ({hull.Indices.Count})");
}
Defensive patterns

Strategy: validation

Validate before calling

var hull = shape as ConvexHullColliderShape;
if (hull != null && hull.Indices.Count % 3 != 0)
    throw new InvalidOperationException("Convex hull indices are not a triangle list; regenerate the hull asset");

Type guard

static bool IsTriangleList(ConvexHullColliderShape hull) => hull != null && hull.Indices.Count % 3 == 0;

Try / catch

try
{
    navigationMeshBuilder.Build();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("does not consist of triangles"))
{
    // identify and fix/regenerate the offending convex hull collider
}

Prevention

When it happens

Trigger: A ConvexHullColliderShape whose Indices.Count is not a multiple of 3 — usually from a hull generated by a broken/incompatible V-HACD version or truncated serialization of the convex hull asset.

Common situations: Convex hull assets regenerated by an external tool with a different index layout; assets serialized by an older Stride version then loaded by a newer one; manually constructed hulls with line/point indices mixed in.

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

Appendix: source

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

                            Plane plane = new Plane(planeDesc.Normal, planeDesc.Offset)
                            {
                                // Pre-Transform plane parameters
                                Normal = Vector3.TransformNormal(planeDesc.Normal, transform)
                            };
                            plane.Normal.Normalize();
                            plane.D += Vector3.Dot(transform.TranslationVector, plane.Normal);

                            colliderData.Planes.Add(plane);
                        }
                        else if (shapeType == typeof(ConvexHullColliderShape))
                        {
                            var hull = (ConvexHullColliderShape)shape;
                            Matrix transform = hull.PositiveCenterMatrix * entityWorldMatrix;

                            // V-HACD emits hull triangles in left-handed winding, matching the navmesh input convention.
                            int[] indices = new int[hull.Indices.Count];
                            if (hull.Indices.Count % 3 != 0) throw new InvalidOperationException($"{shapeType} does not consist of triangles");
                            for (int i = 0; i < hull.Indices.Count; i++)
                            {
                                indices[i] = (int)hull.Indices[i];
                            }

                            entityNavigationMeshInputBuilder.AppendArrays(hull.Points.ToArray(), indices, transform);
                        }
                        else if (shapeType == typeof(StaticMeshColliderShape))
                        {
                            var mesh = (StaticMeshColliderShape)shape;
                            Matrix transform = mesh.PositiveCenterMatrix * entityWorldMatrix;

                            mesh.GetMeshDataCopy(out var verts, out var indices);

                            // Convert hull indices to int
                            if (indices.Length % 3 != 0) throw new InvalidOperationException($"{shapeType} does not consist of triangles");
                            for (int i = 0; i < indices.Length; i += 3)
                            {

View on GitHub (pinned to 96fad776d2)