stride3d/stride · error · InvalidOperationException
The type of dictionary key must be a primary type.
Error message
The type of dictionary key must be a primary type.
What it means
DefaultNodeBuilder.VisitDictionary throws InvalidOperationException because Quantum represents dictionary entries through node indexes, which require key types the primitive filter can handle. Dictionary keys must be primitive types (string, numeric, enum-like primitives) to be addressable in the graph.
Solutions
- Change the dictionary key to a primitive type such as string or int
- Convert complex keys to strings (e.g. serialize the key) before storing them in an observed dictionary
- Exclude the dictionary from the node graph if complex keys are required
- Catch InvalidOperationException and surface a clear model error to the developer
Example fix
// before public Dictionary<MyKeyStruct, Item> Items = new Dictionary<MyKeyStruct, Item>(); // after public Dictionary<string, Item> Items = new Dictionary<string, Item>();
Defensive patterns
Strategy: validation
Validate before calling
var d = TypeDescriptorFactory.Default.Find(dict.GetType()); if (d is DictionaryDescriptor dd && !PrimitiveTypeFilter.IsPrimitiveType(dd.KeyType)) throw new InvalidOperationException($"Key type {dd.KeyType} is not primitive"); Try / catch
try { container.Build(root); } catch (InvalidOperationException ex) when (ex.Message.Contains("dictionary key")) { logger.LogError(ex, "Non-primitive dictionary key in model"); } Prevention
- Use string or int dictionary keys in Quantum-observed models
- Serialize complex keys to strings before storing
- Validate dictionary key types during model design review
When it happens
Trigger: Building a graph over a Dictionary<MyCustomClass, T> or any dictionary whose KeyType is not recognized by PrimitiveTypeFilter.IsPrimitiveType during VisitDictionary.
Common situations: Using enum-typed keys beyond the primitive filter, custom struct keys, or object keys in models observed by Quantum; config models with typed-key dictionaries.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- A collection type
- A node of the path is null but is not the last node.
- A reference can only be constructed with the method…
- An error occurred while adding an item to the node, see the…
- An error occurred while adding an item to the node, see the…
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/598a5ccbcc2993cf.
Report an issue: GitHub.
Appendix: source
Thrown at sources/presentation/Stride.Core.Quantum/DefaultNodeBuilder.cs:104
/// <inheritdoc/>
public override void VisitCollection(IEnumerable collection, CollectionDescriptor descriptor)
{
if (!descriptor.HasIndexerAccessors)
throw new NotSupportedException("Collections that do not have indexer accessors are not supported in Quantum.");
// Don't visit items unless they are primitive or enumerable (collections within collections)
if (IsCollection(descriptor.ElementType))
{
base.VisitCollection(collection, descriptor);
}
}
/// <inheritdoc/>
public override void VisitDictionary(object dictionary, DictionaryDescriptor descriptor)
{
if (!PrimitiveTypeFilter.IsPrimitiveType(descriptor.KeyType))
throw new InvalidOperationException("The type of dictionary key must be a primary type.");
// Don't visit items unless they are primitive or enumerable (collections within collections)
if (IsCollection(descriptor.ValueType))
{
base.VisitDictionary(dictionary, descriptor);
}
}
/// <inheritdoc/>
public override void VisitObjectMember(object container, ObjectDescriptor containerDescriptor, IMemberDescriptor member, object? value)
{
// If this member should contain a reference, create it now.
var containerNode = (IInitializingObjectNode)GetContextNode();
var guid = Guid.NewGuid();
var content = (MemberNode)NodeFactory.CreateMemberNode(this, guid, containerNode, member, value);
containerNode.AddMember(content);
PushContextNode(content);View on GitHub (pinned to 96fad776d2)