MonoGame/MonoGame · error · NotImplementedException

Unhandled primitive type `{type.FullName}`!

Error message

Unhandled primitive type `{type.FullName}`!

What it means

GetTypeSerializer falls back to the ReflectiveSerializer for unknown types, but reflection-based serialization is intentionally forbidden for CLR primitives. If a primitive numeric/pointer type has no registered ContentTypeSerializer, the lookup throws NotImplementedException rather than mis-serializing it by reflection.

Source

Thrown at MonoGame.Framework.Content.Pipeline/Serialization/Intermediate/IntermediateSerializer.cs:175

            {
                serializerType = serializerType.MakeGenericType(type.GetGenericArguments());
                serializer = (ContentTypeSerializer)Activator.CreateInstance(serializerType)!;
            }
            else if (type.IsEnum)
            {
                serializer = new EnumSerializer(type);
            }
            else if (typeof(IList).IsAssignableFrom(type) && !GenericCollectionHelper.IsGenericCollectionType(type, true))
            {
                // Special handling for non-generic IList types. By the time we get here,
                // generic collection types will already have been handled by one of the known serializers.
                serializer = new NonGenericIListSerializer(type);
            }
            else
            {
                // The reflective serializer is not for primitive types!
                if (type.IsPrimitive)
                    throw new NotImplementedException($"Unhandled primitive type `{type.FullName}`!");

                // We still don't have a serializer then we
                // fallback to the reflection based serializer.
                serializer = new ReflectiveSerializer(type);
            }

            Debug.Assert(serializer.TargetType == type, "Target type mismatch!");

            // We cache the serializer before we initialize it to
            // avoid a stack overflow on recursive types.
            _serializers.Add(type, serializer);
            serializer.Initialize(this);

            return serializer;
        }

        internal GenericCollectionHelper GetCollectionHelper(Type type)
        {

View on GitHub (pinned to 1d71bbd0ff)

Solutions

  1. Do not serialize native pointer/handle primitives; wrap them in a non-primitive type or exclude them with [ContentSerializerIgnore].
  2. Store the value as a serializable `long`/`ulong` and convert to IntPtr at use sites.
  3. Register a custom ContentTypeSerializer for the primitive type if serialization is genuinely required.

Example fix

// before
public class GpuResource
{
    public IntPtr Handle { get; set; }
}

// after
public class GpuResource
{
    public long HandleValue { get; set; }
    [ContentSerializerIgnore]
    public IntPtr Handle { get => (IntPtr)HandleValue; set => HandleValue = (long)value; }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Reject serializable members whose primitive type has no registered serializer
var ptrTypes = typeof(T).GetFields()
    .Where(f => f.FieldType.IsPrimitive && !typeof(bool).IsAssignableFrom(f.FieldType)
        && !new[] { typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int),
            typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(char) }
            .Contains(f.FieldType));
if (ptrTypes.Any())
    throw new NotSupportedException("Unserializable primitive member: " + string.Join(", ", ptrTypes.Select(f => f.Name)));

Type guard

static bool IsSerializablePrimitive(Type t) =>
    !t.IsPrimitive || new[] { typeof(bool), typeof(byte), typeof(sbyte), typeof(short),
        typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong),
        typeof(float), typeof(double), typeof(char) }.Contains(t);

Try / catch

try { IntermediateSerializer.Serialize(writer, value, path); }
catch (NotImplementedException ex) when (ex.Message.Contains("Unhandled primitive"))
{
    // replace the IntPtr/nint member with a long, or mark it [ContentSerializerIgnore]
}

Prevention

When it happens

Trigger: A type graph containing a primitive type for which no explicit serializer is registered. In practice this is `IntPtr`/`UIntPtr`/`nint`/`nuint` (which report IsPrimitive == true in modern .NET) used as a serializable field/property, since int/float/double/etc. all have dedicated serializers.

Common situations: Adding a native-handle-sized field (`IntPtr handle`) to a pipeline-serializable class, or targeting a runtime where a previously-registered primitive serializer was removed.

Related errors


AI-assisted analysis of MonoGame/MonoGame@1d71bbd0ff (2026-08-13). Data as JSON: /api/errors/3007507b14454fda. Report an issue: GitHub.