stride3d/stride · error · InvalidOperationException

Can't deduce data serializer type for generic types.

Error message

Can't deduce data serializer type for generic types.

What it means

ProcessDataSerializerGlobalAttributes deduces which data type a [DataSerializer]-attributed serializer serves. When the serializer is generic (mode != None) and no data type was explicitly supplied, the type cannot be deduced, so this error is thrown.

Solutions

  1. Ensure the generic serializer inherits a closed generic base like DataSerializer<List<T>> so the data type is inferable
  2. Specify the data type explicitly via DataSerializer attribute on the target type instead of global attribute scanning
  3. Remove the DataSerializerGenericMode if the serializer is actually non-generic
  4. Fix the serializer class declaration so FindSerializerDataType can walk its base types

Example fix

// before
class MySerializer : DataSerializer<List<T>> { } // no generic mode param flow
// after
[DataSerializerGenericMode(DataSerializerGenericMode.GenericArityAndArguments)]
class MySerializer<T> : DataSerializer<List<T>> { }
Defensive patterns

Strategy: type-guard

Validate before calling

static bool CanDeduceSerializerType(Type serializerType)
{
    var baseType = serializerType.BaseType;
    while (baseType != null && !baseType.IsConstructedGenericType == false)
    {
        if (baseType.GetGenericTypeDefinition() == typeof(DataSerializer<>))
            return !baseType.ContainsGenericParameters;
        baseType = baseType.BaseType;
    }
    return false;
}

Type guard

static bool IsGenericModeSerializer(Type t) =>
    t.GetCustomAttribute<DataSerializerGenericModeAttribute>()?.Mode != DataSerializerGenericMode.None;

Try / catch

try { ProcessDataSerializer(serializerType); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Can't deduce data serializer type"))
{
    logger.Error($"{serializerType} is generic but its data type cannot be deduced; close the generic base type");
}

Prevention

When it happens

Trigger: A DataSerializer subclass declares DataSerializerGenericMode (e.g. GenericArityAndArguments) but ProcessDataSerializerGlobalAttributes reaches it with dataType == null and cannot infer the target type from FindSerializerDataType.

Common situations: A generic serializer whose base DataSerializer<T> type cannot be resolved to a closed type; missing generic arguments on the serializer class; misconfigured [DataSerializer] usage.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at sources/core/Stride.Core.AssemblyProcessor/Serializers/ReferencedAssemblySerializerProcessor.cs:72

        // Find DataSerializer attribute on assembly and/or types
        foreach (var dataSerializerAttribute in
            assembly.CustomAttributes.Concat(assembly.MainModule.GetAllTypes().SelectMany(x => x.CustomAttributes)).Where(
                x => x.AttributeType.FullName == "Stride.Core.Serialization.DataSerializerGlobalAttribute")
                .OrderBy(x => x.ConstructorArguments[0].Value != null ? -1 : 1)) // Order so that we first have the ones which don't require us to go through GenerateSerializer
        {
            var dataSerializerType = (TypeReference)dataSerializerAttribute.ConstructorArguments[0].Value;
            var dataType = (TypeReference)dataSerializerAttribute.ConstructorArguments[1].Value;
            var mode = (DataSerializerGenericMode)dataSerializerAttribute.ConstructorArguments[2].Value;
            var inherited = (bool)dataSerializerAttribute.ConstructorArguments[3].Value;
            var complexSerializer = (bool)dataSerializerAttribute.ConstructorArguments[4].Value;
            var profile = dataSerializerAttribute.Properties.Where(x => x.Name == "Profile").Select(x => (string)x.Argument.Value).FirstOrDefault() ?? "Default";

            if (dataType == null)
            {
                if (mode == DataSerializerGenericMode.None)
                    dataType = FindSerializerDataType(dataSerializerType);
                else
                    throw new InvalidOperationException("Can't deduce data serializer type for generic types.");
            }

            // Reading from custom arguments doesn't have its ValueType properly set
            dataType = dataType.FixupValueType();
            dataSerializerType = dataSerializerType?.FixupValueType();

            CecilSerializerContext.SerializableTypeInfo serializableTypeInfo;

            if (dataSerializerType == null)
            {
                // TODO: We should avoid calling ResolveSerializer now just to have the dataSerializerType (we should do so only in a second step)
                serializableTypeInfo = context.ResolveSerializer(dataType, profile: profile);
                if (serializableTypeInfo == null)
                    throw new InvalidOperationException(string.Format("Can't find serializer for type {0}", dataType));
                serializableTypeInfo.Local = local;
                serializableTypeInfo.ExistingLocal = local;
                dataSerializerType = serializableTypeInfo.SerializerType;
            }

View on GitHub (pinned to 96fad776d2)