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

  1. Change the dictionary key to a primitive type such as string or int
  2. Convert complex keys to strings (e.g. serialize the key) before storing them in an observed dictionary
  3. Exclude the dictionary from the node graph if complex keys are required
  4. 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

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


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)