stride3d/stride · error · InvalidOperationException

Collection is read only

Error message

Collection is read only

What it means

StrideToBulletWrapper is a read-only IList<BulletSharp.Math.Vector3> adapter that maps Stride vectors to Bullet vectors for the physics backend. Mutating members are explicitly implemented to throw InvalidOperationException('Collection is read only') because the wrapper forwards to an immutable underlying buffer.

Solutions

  1. Build the vertex data in a regular List<BulletSharp.Math.Vector3> first, then construct the StaticMeshColliderShape from it
  2. Treat the collection as read-only — do not call Add/Remove/Clear on it; use indexing/enumeration only
  3. Replace the wrapper usage with a mutable collection type if mutation is genuinely required

Example fix

// before
var points = staticMeshShape.Points; // StrideToBulletWrapper, read-only
points.Add(new Vector3(1, 2, 3)); // throws

// after
var points = new List<BulletSharp.Math.Vector3> { new Vector3(1, 2, 3) };
var shape = new StaticMeshColliderShape(points, indices);
Defensive patterns

Strategy: type-guard

Validate before calling

if (points is ICollection<BulletSharp.Math.Vector3> c && c.IsReadOnly)
    throw new NotSupportedException("Point collection is read-only; build data elsewhere first");

Type guard

bool IsMutableVectorList(object coll) =>
    coll is ICollection<BulletSharp.Math.Vector3> c && !c.IsReadOnly;

Try / catch

try { points.Add(item); }
catch (InvalidOperationException ex) when (ex.Message == "Collection is read only")
{
    var buffer = new List<BulletSharp.Math.Vector3>(points) { item };
    shape = new StaticMeshColliderShape(buffer, indices);
}

Prevention

When it happens

Trigger: Calling Add on a Vector3 collection obtained through StrideToBulletWrapper — e.g. attempting to add vertices to a static mesh collider's point collection after creation.

Common situations: Treating the collider's vertex collection like a growable List<T>; code written for mutable collections (ICollection.Add) being reused against the read-only wrapper; refactoring that changed a List into the Bullet wrapper.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/7d402d194b13d8fc. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Physics/Shapes/StaticMeshColliderShape.cs:257

            }

            public int Count => internalColl.Count;

            public bool IsReadOnly => true;

            public bool Contains(BulletSharp.Math.Vector3 item) => internalColl.Contains(item);

            public void CopyTo(BulletSharp.Math.Vector3[] array, int arrayIndex)
            {
                foreach (var value in internalColl)
                {
                    if (arrayIndex >= array.Length)
                        return;
                    array[arrayIndex++] = value;
                }
            }

            public void Add(BulletSharp.Math.Vector3 item) => throw new InvalidOperationException("Collection is read only");
            public bool Remove(BulletSharp.Math.Vector3 item) => throw new InvalidOperationException("Collection is read only");
            public void Clear() => throw new InvalidOperationException("Collection is read only");

            public IEnumerator<BulletSharp.Math.Vector3> GetEnumerator()
            {
                foreach (var value in internalColl)
                    yield return value;
            }

            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        }
    }
}

View on GitHub (pinned to 96fad776d2)