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
- Ensure the generic serializer inherits a closed generic base like DataSerializer<List<T>> so the data type is inferable
- Specify the data type explicitly via DataSerializer attribute on the target type instead of global attribute scanning
- Remove the DataSerializerGenericMode if the serializer is actually non-generic
- 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
- Generic serializers must derive from a closed DataSerializer<T> shape like DataSerializer<List<T>>
- Set [DataSerializerGenericMode] on generic serializer classes
- Avoid abstract open-generic intermediate base classes between serializer and DataSerializer<T>
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
- Unsupported generic resolution.
- Serialization of nested types referencing parent's generic…
- Incompatible serializer found for same type in different…
- Could not find serializer for generic dependent type
- Could not determine data type for
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)