stride3d/stride · error · QuantumConsistencyException

A collection type

Error message

A collection type

What it means

DefaultNodeBuilder.VisitObject throws QuantumConsistencyException when a node created from an object turns out to be a reference node while its descriptor type is a struct. Quantum expects struct-typed values to be boxed/plain value nodes, not reference nodes, so this signals an internal node-factory/type-descriptor inconsistency.

Solutions

  1. Inspect the struct type whose descriptor sets IsReference and fix the custom descriptor/factory so structs are not treated as references
  2. Update Stride.Core packages so Quantum and TypeDescriptor versions match
  3. If the type should be a class, change the model so the descriptor's type is not a struct
  4. Catch QuantumConsistencyException during Build and log the offending type to identify the misregistered descriptor

Example fix

// before: custom factory returns a reference node for a struct
public override IObjectNode CreateObjectNode(...) => new ObjectNode(..., isReference: true);
// after: let the default factory box value types
var content = descriptor.Type.IsValueType
    ? NodeFactory.CreateBoxedNode(this, rootGuid, obj, descriptor)
    : NodeFactory.CreateObjectNode(this, rootGuid, obj, descriptor);
Defensive patterns

Strategy: validation

Validate before calling

if (descriptor.Type.IsValueType && descriptor.IsReference) throw new InvalidOperationException($"{descriptor.Type} is a struct marked as reference");

Type guard

bool IsValidStructNode(ITypeDescriptor d) => !(d.Type.IsValueType && d.IsReference);

Try / catch

try { builder.Build(obj); } catch (QuantumConsistencyException ex) { logger.LogError(ex, "Quantum inconsistency for {Type}", ex.Object?.GetType()); }

Prevention

When it happens

Trigger: Visiting an object whose TypeDescriptor reports IsValueType (struct) but NodeFactory.CreateBoxedNode/CreateObjectNode produces a node with IsReference=true, e.g. a type whose descriptor is inconsistent (custom ITypeDescriptor, mismatched metadata) during Build of a NodeContainer.

Common situations: Custom type descriptors or node factories overriding default behavior; struct types registered or resolved through descriptors that mark them as reference types; library version mismatch where IsReference detection changed.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/6fbb237ef970178a. Report an issue: GitHub.

Appendix: source

Thrown at sources/presentation/Stride.Core.Quantum/DefaultNodeBuilder.cs:70

        return rootNode!;
    }

    /// <inheritdoc/>
    public override void VisitObject(object obj, ObjectDescriptor descriptor, bool visitMembers)
    {
        ITypeDescriptor currentDescriptor = descriptor;

        bool isRootNode = contextStack.Count == 0;
        if (isRootNode)
        {
            // If we're visiting a value type as "object" we need to use a special "boxed" node.
            var content = descriptor.Type.IsValueType ? NodeFactory.CreateBoxedNode(this, rootGuid, obj, descriptor)
                : NodeFactory.CreateObjectNode(this, rootGuid, obj, descriptor);

            currentDescriptor = content.Descriptor;
            rootNode = (IInitializingObjectNode)content;
            if (content.IsReference && currentDescriptor.Type.IsStruct())
                throw new QuantumConsistencyException("A collection type", "A structure type", rootNode);

            PushContextNode(rootNode);
        }

        if (!PrimitiveTypeFilter.IsPrimitiveType(currentDescriptor.Type))
        {
            base.VisitObject(obj, descriptor, true);
        }

        if (isRootNode)
        {
            PopContextNode();
            rootNode!.Seal();
        }
    }

    /// <inheritdoc/>
    public override void VisitCollection(IEnumerable collection, CollectionDescriptor descriptor)

View on GitHub (pinned to 96fad776d2)