stride3d/stride · error · InvalidOperationException
An item has been added to a collection that does not have a…
Error message
An item has been added to a collection that does not have a predictable Add. Consider using NonIdentifiableCollectionItemsAttribute on this collection.
What it means
When an item is added to an identifiable collection, AssetObjectNodeExtended records the new item's id at the position reported by the change event. For ordered lists (non-set categories) an add must carry a concrete NodeIndex (predictable Add/Insert); when the index is Empty the library cannot place the id and throws, suggesting NonIdentifiableCollectionItemsAttribute.
Solutions
- Apply [NonIdentifiableCollectionItemsAttribute] to the offending collection property so ids are tracked by index instead of insertion position.
- Replace the collection with a standard List<T> (predictable Add) or a true Set type so the descriptor category matches behavior.
- Implement Add on the custom collection so it appends at a deterministic end index and reports NodeIndex accordingly.
- If the index can be computed, fix the event source to supply a valid e.Index before raising the change.
Example fix
// before
public HashSet<MyItem> Items { get; set; } = new(); // set-like, but descriptor is List category
// after
[NonIdentifiableCollectionItems]
public HashSet<MyItem> Items { get; set; } = new(); Defensive patterns
Strategy: validation
Validate before calling
var cat = collectionDescriptor?.Category; bool needsIndex = cat != DescriptorCategory.Set; bool indexKnown = e.Index != NodeIndex.Empty; bool safe = !needsIndex || indexKnown; // otherwise mark collection non-identifiable
Type guard
static bool SupportsIdentifiableAdd(ICollectionDescriptor d) =>
d == null || d.Category == DescriptorCategory.Set || d is { /* predictable Add */ }; Try / catch
try { nodeExtended.TrackItemChange(e); }
catch (InvalidOperationException) { /* treat collection as non-identifiable */ } Prevention
- Use List<T> or Set types for identifiable collections
- Add [NonIdentifiableCollectionItems] to set-like collections
- Keep descriptor Category consistent with the collection's Add behavior
When it happens
Trigger: An add notification (OnItemChanged with ItemChangeType.Add) arrives with e.Index == NodeIndex.Empty on a collection whose descriptor Category is not Set — i.e. a list-like collection whose Add does not report where the item lands (e.g. HashSet-like or custom collection without predictable Add).
Common situations: Using a collection type whose Add doesn't append at a known index (dict-like or set-like behavior without Set descriptor); a custom collection added to an asset without the NonIdentifiableCollectionItemsAttribute; descriptor category changed between versions.
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
- No Collection item identifier associated to the given…
- Unable to find the base
- Unable to find the graph corresponding to the base part
- The base is unset for the current node.
- An IObjectNode was expected when processing the path
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/bc63d4349b954e1c.
Report an issue: GitHub.
Appendix: source
Thrown at sources/assets/Stride.Core.Assets.Quantum/Internal/AssetObjectNodeExtended.cs:344
newValue = setDescriptor.ElementType.Default();
}
if (!itemIds.ContainsKey(newValue))
{
var itemId = restoringId != ItemId.Empty ? restoringId : ItemId.New();
itemIds[newValue] = itemId;
}
}
break;
}
case ContentChangeType.CollectionAdd:
{
// Compute the id we will add for this item
var itemId = restoringId != ItemId.Empty ? restoringId : ItemId.New();
// Add the id to the proper location (insert or add)
if (collectionDescriptor is not null && collectionDescriptor.Category != DescriptorCategory.Set)
{
if (e.Index == NodeIndex.Empty)
throw new InvalidOperationException("An item has been added to a collection that does not have a predictable Add. Consider using NonIdentifiableCollectionItemsAttribute on this collection.");
itemIds.Insert(e.Index.Int, itemId);
}
else
{
itemIds[e.Index.Value] = itemId;
}
break;
}
case ContentChangeType.CollectionRemove:
{
var itemId = itemIds[e.Index.Value];
// update isOverriding, it should be true only if the item being removed exist in the base.
isOverriding = isOverriding && (baseNode?.HasId(itemId) ?? false);
if (collectionDescriptor is not null && collectionDescriptor.Category != DescriptorCategory.Set)
{
removedId = itemIds.DeleteAndShift(e.Index.Int, isOverriding);
}View on GitHub (pinned to 96fad776d2)