stride3d/stride · error · InvalidOperationException
Cannot deserialize collection to readonly collection type
Error message
Cannot deserialize collection to readonly collection type [{thisObject.GetType()}]. What it means
CollectionSerializer clears the target collection and re-adds deserialized items; if the collection instance reports itself read-only (CollectionDescriptor.IsReadOnly), items cannot be replaced, so this InvalidOperationException is thrown before reading items.
Solutions
- Change the member type to a writable collection (List<T>, T[], HashSet<T>, Dictionary<K,V>)
- Initialize the member to a new mutable instance instead of a read-only wrapper
- Add a DataContract/MemberWidth renaming or custom serializer for read-only collections
- If the type is fixed, implement a custom serializer registered in the factory
Example fix
// before
public IReadOnlyList<int> Items { get; } = Array.AsReadOnly(new int[0]);
// after
public List<int> Items { get; set; } = new List<int>(); Defensive patterns
Strategy: validation
Validate before calling
var descriptor = TypeDescriptorFactory.Current.FindDescriptor(memberType) as CollectionDescriptor;
if (descriptor?.IsReadOnly(instance) == true)
throw new InvalidOperationException($"{memberType} is read-only; use a writable collection for deserialization"); Type guard
static bool IsWritableCollection<T>(T c) where T: System.Collections.ICollection => !c.IsReadOnly;
Try / catch
try
{
serializer.ReadYaml(ref objectContext);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("readonly collection"))
{
// re-initialize the member with a mutable instance and retry
} Prevention
- Avoid IReadOnlyList/ReadOnlyCollection members in serialized object graphs
- Initialize collection members with new List<T>() instead of read-only wrappers
- Keep serialized model properties simple and mutable
When it happens
Trigger: Deserializing YAML into a collection instance that is read-only — e.g. property typed as IReadOnlyList<T>/IReadOnlyCollection<T>, an array-backed fixed collection, or a collection exposing IsReadOnly == true (Collection<T>.IsReadOnly, frozen collections).
Common situations: Using read-only collection interfaces as member types in YAML-serialized models; passing Array.AsReadOnly()/FrozenSet results into serialized object graphs; API changes where a property type became read-only.
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 type
- Cannot add item with key
- Cannot instantiate an object for type
- Cannot set member [ . ]
- Collection is read only
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/bcd64c01e2ad3bec.
Report an issue: GitHub.
Appendix: source
Thrown at sources/core/Stride.Core.Yaml/Serialization/Serializers/CollectionSerializer.cs:148
/// <summary>
/// Reads the collection items.
/// </summary>
/// <param name="objectContext">The object context.</param>
/// <exception cref="System.InvalidOperationException">Cannot deserialize list to type [{0}]. No Add method found.DoFormat(thisObject.GetType())
/// or
/// Cannot deserialize list to readonly collection type [{0}]..DoFormat(thisObject.GetType())</exception>
protected virtual void ReadCollectionItems(ref ObjectContext objectContext)
{
var collectionDescriptor = (CollectionDescriptor) objectContext.Descriptor;
var thisObject = objectContext.Instance;
if (!collectionDescriptor.HasAdd)
{
throw new InvalidOperationException($"Cannot deserialize collection to type [{thisObject.GetType()}]. No Add method found");
}
if (collectionDescriptor.IsReadOnly(thisObject))
{
throw new InvalidOperationException($"Cannot deserialize collection to readonly collection type [{thisObject.GetType()}].");
}
// The collection may contain some initial data from its containing instance's ctor,
// let's replace the existing data with the data we have serialized
collectionDescriptor.Clear(thisObject);
var reader = objectContext.Reader;
var elementType = collectionDescriptor.ElementType;
var index = 0;
while (!reader.Accept<SequenceEnd>())
{
var currentDepth = objectContext.Reader.CurrentDepth;
var startParsingEvent = objectContext.Reader.Parser.Current;
try
{
ReadAddCollectionItem(ref objectContext, elementType, collectionDescriptor, thisObject, index);View on GitHub (pinned to 96fad776d2)