stride3d/stride · error · InvalidOperationException
The given container does not match the expected type.
Error message
The given container does not match the expected type.
What it means
After deserialization, CollectionWithIdsSerializer.TransformAfterDeserialization checks that the intermediate container is an instance of the expected closed generic CollectionWithItemIds<T> before copying item ids into the real collection. A mismatched container means the pipeline produced the wrong intermediate object, so it throws InvalidOperationException to prevent silently losing per-item ids.
Solutions
- Ensure the container passed through the chain is created by CreatEmptyContainer (CollectionWithItemIds<T>) and not replaced
- Remove or fix custom chained serializers that swap the intermediate container
- Verify target descriptor's element type matches the one used to build the container
- Revert to stock Stride serializer classes for asset collections
Example fix
// before IDictionary container = new Dictionary<object, object>(); // wrong type serializer.TransformObjectAfterRead(container, descriptor, target); // after var container = serializerInstance.CreateContainerForTest(descriptor); // produces CollectionWithItemIds<T> serializer.TransformObjectAfterRead(container, descriptor, target);
Defensive patterns
Strategy: type-guard
Validate before calling
var expectedType = typeof(CollectionWithItemIds<>).MakeGenericType(elementType);
if (!expectedType.IsInstanceOfType(container))
throw new InvalidOperationException($"Container must be {expectedType}, got {container?.GetType()}"); Type guard
static bool IsCollectionWithItemIdsOf(IDictionary? c, Type elementType) =>
c != null && typeof(CollectionWithItemIds<>).MakeGenericType(elementType).IsInstanceOfType(c); Try / catch
try { serializer.RunTransform(container, descriptor, target); }
catch (InvalidOperationException ex) when (ex.Message.Contains("does not match the expected type"))
{
Log.Error("Intermediate collection container replaced in chain: {Message}", ex.Message);
throw;
} Prevention
- Always obtain intermediate containers from the serializer's own CreatEmptyContainer
- Don't insert custom chained serializers that swap IDictionary intermediates
- Verify element types haven't changed between container creation and transformation
- Pin Stride serializer classes; avoid subclassing them in plugins
When it happens
Trigger: A custom/derived serializer returning a different IDictionary container; chain changes so CreatEmptyContainer and TransformAfterDeserialization disagree; serializing an object whose descriptor element type changed between container creation and transform.
Common situations: Patched serializers or custom chained serializers for assets; version-skew between plugin code building containers and the Stride asset serializer; regression tests feeding hand-built dictionaries into the transform step.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Event handlers can't be added or removed after the…
- RoutingSerializer expected in the chain of serializers
- The type of collection does not have a parameterless…
- The type of dictionary does not have a parameterless…
- Unable to extract url reference from object
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/fe9f98754557a4ca.
Report an issue: GitHub.
Appendix: source
Thrown at sources/assets/Stride.Core.Assets/Yaml/CollectionWithIdsSerializer.cs:114
}
/// <inheritdoc/>
protected override IDictionary CreatEmptyContainer(ITypeDescriptor descriptor)
{
var collectionDescriptor = (CollectionDescriptor)descriptor;
var type = typeof(CollectionWithItemIds<>).MakeGenericType(collectionDescriptor.ElementType);
if (type.GetConstructor(Type.EmptyTypes) == null)
throw new InvalidOperationException("The type of collection does not have a parameterless constructor.");
return (IDictionary)Activator.CreateInstance(type)!;
}
/// <inheritdoc/>
protected override void TransformAfterDeserialization(IDictionary container, ITypeDescriptor targetDescriptor, object targetCollection, ICollection<ItemId>? deletedItems = null)
{
var collectionDescriptor = (CollectionDescriptor)targetDescriptor;
var type = typeof(CollectionWithItemIds<>).MakeGenericType(collectionDescriptor.ElementType);
if (!type.IsInstanceOfType(container))
throw new InvalidOperationException("The given container does not match the expected type.");
var identifier = CollectionItemIdHelper.GetCollectionItemIds(targetCollection);
identifier.Clear();
// 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(targetCollection);
var i = 0;
var enumerator = container.GetEnumerator();
while (enumerator.MoveNext())
{
collectionDescriptor.Add(targetCollection, enumerator.Value);
if (targetDescriptor.Category == DescriptorCategory.Set)
{
identifier.Add(enumerator.Value!, (ItemId)enumerator.Key);
}
else
{View on GitHub (pinned to 96fad776d2)