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
- Build the vertex data in a regular List<BulletSharp.Math.Vector3> first, then construct the StaticMeshColliderShape from it
- Treat the collection as read-only — do not call Add/Remove/Clear on it; use indexing/enumeration only
- 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
- Treat any collection exposed by a collider shape as read-only; compose vertex data in your own List first
- Check ICollection.IsReadOnly before calling Add/Remove/Clear on any physics collection
- Never mutate mesh data after the collider shape has been created — rebuild the shape instead
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
- Cannot deserialize collection to readonly collection type
- Cannot perform this action when the physics engine is set…
- A category cannot be deleted
- A Gear constraint always needs two rigidbodies to be…
- A Gear constraint requires two rigidbodies.
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)