stride3d/stride · error · InvalidOperationException

Cannot deserialize collection to type

Error message

Cannot deserialize collection to type [{thisObject.GetType()}]. No Add method found

What it means

CollectionSerializer needs the target collection type to expose an Add method to populate it during YAML deserialization. The type resolved for the collection node has no usable Add method, so deserialization cannot proceed and this InvalidOperationException is thrown.

Solutions

  1. Give the collection type a public Add(item) method compatible with the descriptor
  2. Deserialization target should be a mutable collection (List<T>, array, Dictionary, etc.)
  3. Write a custom serializer for the type and register it with the SerializerFactory
  4. Deserialize into a mutable type and expose the immutable one separately

Example fix

// before
public class MyCollection { public void AddRange(IEnumerable<int> items) { ... } }
// after
public class MyCollection : IEnumerable<int>
{
    private readonly List<int> _items = new();
    public void Add(int item) => _items.Add(item); // required
    public IEnumerator<int> GetEnumerator() => _items.GetEnumerator();
    ...
}
Defensive patterns

Strategy: validation

Validate before calling

var descriptor = TypeDescriptorFactory.Current.FindDescriptor(collectionType) as CollectionDescriptor;
if (descriptor != null && !descriptor.HasAdd)
    throw new InvalidOperationException($"{collectionType} lacks an Add method; use a mutable collection");

Type guard

static bool DeserializableAsCollection(Type t) =>
    typeof(System.Collections.IEnumerable).IsAssignableFrom(t)
    && t.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance) != null;

Try / catch

try
{
    serializer.ReadYaml(ref objectContext);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("No Add method found"))
{
    // deserialize into List<T> or register a custom serializer
}

Prevention

When it happens

Trigger: Deserializing a YAML mapping/sequence into a collection type whose CollectionDescriptor reports HasAdd == false — e.g. a custom collection with only AddRange, an immutable collection, or a type without a public Add.

Common situations: Switching a property type to a read-only/immutable collection (ImmutableList, IReadOnlyCollection wrappers); custom collection classes lacking a public Add; third-party collection types without Add.

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/c34712f5cd796540. Report an issue: GitHub.

Appendix: source

Thrown at sources/core/Stride.Core.Yaml/Serialization/Serializers/CollectionSerializer.cs:144

                objectContext.Writer.Emit(new SequenceEndEventInfo(objectContext.Instance, objectContext.Instance.GetType()));
            }
        }

        /// <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;

View on GitHub (pinned to 96fad776d2)