stride3d/stride · error · ArgumentException

Type [{0}] is not a primitive

Error message

Type [{0}] is not a primitive

What it means

The NullableDescriptor constructor only accepts Nullable<T> types (i.e. typeof(int?) and similar); it verifies with IsNullable(type) and throws ArgumentException 'Type [{0}] is not a primitive' when a non-nullable type is passed. Note the message uses '{0}' without string.Format, so the raw placeholder is displayed. This is an internal TypeDescriptor factory invariant — descriptors are built per-type by TypeDescriptorFactory.

Solutions

  1. Check Nullable.GetUnderlyingType(type) != null before constructing NullableDescriptor.
  2. Route non-nullable types to ObjectDescriptor/PrimitiveDescriptor via the normal TypeDescriptorFactory instead.
  3. Fix the dispatch predicate so only Nullable<T> types reach this constructor.

Example fix

// before
var descriptor = new NullableDescriptor(factory, typeof(int), false, namingConvention);
// after
if (Nullable.GetUnderlyingType(typeof(int?)) != null)
    var descriptor = new NullableDescriptor(factory, typeof(int?), false, namingConvention);
Defensive patterns

Strategy: validation

Validate before calling

if (Nullable.GetUnderlyingType(type) == null)
    throw new InvalidOperationException($"{type} is not Nullable<T>; use the appropriate descriptor");
var d = new NullableDescriptor(factory, type, emitDefaultValues, namingConvention);

Type guard

static bool IsNullableType(Type t) => Nullable.GetUnderlyingType(t) != null;

Try / catch

try { return new NullableDescriptor(factory, type, emit, conv); }
catch (ArgumentException) { return TypeDescriptorFactory.Default.FindDescriptor(type); }

Prevention

When it happens

Trigger: Passing a non-Nullable<T> type (e.g. int, string, or a custom struct not wrapped in Nullable<>) to NullableDescriptor's constructor, or having the descriptor factory route a type to NullableDescriptor incorrectly.

Common situations: Custom TypeDescriptorFactory registrations or reflection-based asset serialization scanning where type dispatch logic misclassifies a type as nullable; hand-constructed descriptors in unit tests.

Related errors


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

Appendix: source

Thrown at sources/core/Stride.Core.Reflection/TypeDescriptors/NullableDescriptor.cs:25

/// <summary>
/// Describes a descriptor for a nullable type <see cref="Nullable{T}"/>.
/// </summary>
public class NullableDescriptor : ObjectDescriptor
{
    private static readonly List<IMemberDescriptor> EmptyMembers = [];

    /// <summary>
    /// Initializes a new instance of the <see cref="ObjectDescriptor" /> class.
    /// </summary>
    /// <param name="factory">The factory.</param>
    /// <param name="type">The type.</param>
    /// <exception cref="ArgumentException">Type [{0}] is not a primitive</exception>
    public NullableDescriptor(ITypeDescriptorFactory factory, Type type, bool emitDefaultValues, IMemberNamingConvention namingConvention)
        : base(factory, type, emitDefaultValues, namingConvention)
    {
        if (!IsNullable(type))
            throw new ArgumentException("Type [{0}] is not a primitive");

        UnderlyingType = Nullable.GetUnderlyingType(type)!;
    }

    public override DescriptorCategory Category => DescriptorCategory.Nullable;

    /// <summary>
    /// Gets the type underlying type T of the nullable <see cref="Nullable{T}"/>
    /// </summary>
    /// <value>The type of the element.</value>
    public Type UnderlyingType { get; }

    /// <summary>
    /// Determines whether the specified type is nullable.
    /// </summary>
    /// <param name="type">The type.</param>
    /// <returns><c>true</c> if the specified type is nullable; otherwise, <c>false</c>.</returns>
    public static bool IsNullable(Type type)

View on GitHub (pinned to 96fad776d2)