stride3d/stride · error · InvalidOperationException

Two different classes have the same DataContract Alias

Error message

Two different classes have the same DataContract Alias [${dataContractAliasEntry.Name}]: ${dataContractAliasEntry.Type} and ${DataContractAliasMapping[dataContractAliasEntry.Name]}

What it means

DataSerializerFactory scans assemblies for [DataContract] types and builds a mapping from each DataContract Alias to its Type. Two different classes declared the same alias, so the mapping would be ambiguous; the factory throws InvalidOperationException instead of silently letting the last-registered type win.

Solutions

  1. Make each [DataContract("Alias")] alias unique across all registered assemblies
  2. If the duplicate is stale, delete or rename the old attribute before re-registering the assembly
  3. Regenerate the serializer assembly (Stride codegen) after fixing aliases, then re-run RegisterSerializationAssembly
  4. If a collision is intentional (same type moved assemblies), keep only one registration or unregister the old assembly first

Example fix

// before
// AssemblyA: [DataContract("MyData")] class MyData { }
// AssemblyB: [DataContract("MyData")] class MyDataVariant { }
// after
// AssemblyB: [DataContract("MyDataVariant")] class MyDataVariant { }
Defensive patterns

Strategy: validation

Validate before calling

// scan registered assemblies for duplicate DataContract aliases before registering
var aliasGroups = assemblies
    .SelectMany(a => a.GetTypes().Where(t => t.GetCustomAttributes<DataContractAliasAttribute>().Any()))
    .SelectMany(t => t.GetCustomAttributes<DataContractAliasAttribute>().Select(attr => (attr.Name, Type: t)));
var dupes = aliasGroups.GroupBy(x => x.Name).Where(g => g.Select(x => x.Type).Distinct().Count() > 1).ToList();
if (dupes.Any()) throw new InvalidOperationException("Duplicate aliases: " + string.Join(",", dupes.Select(d => d.Key)));

Try / catch

try
{
    Serialization.RegisterSerializationAssembly(assembly);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("DataContract Alias"))
{
    logger.Fatal(ex, "DataContract alias collision - fix the [DataContract] aliases listed above");
    throw;
}

Prevention

When it happens

Trigger: Registering two assemblies (RegisterSerializers, via RegisterSerializationAssembly/UnregisterSerializationAssembly) that each contain a [DataContract("SameAlias")] on different types; adding a DataContract alias that duplicates one in a referenced module; stale serializer assembly generated from an older alias still being loaded.

Common situations: Renaming a class but leaving its old DataContract alias in place while a new class takes the same alias; two plugins shipping identically-named custom data-contract types; copy-pasting a component class (including its DataContract attribute) into another assembly without changing the alias; assembly regeneration/registration ordering after a version upgrade.

Related errors


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

Appendix: source

Thrown at sources/core/Stride.Core/Serialization/DataSerializerFactory.cs:243

        {
            AvailableAssemblySerializers.TryGetValue(assembly, out var assemblySerializers);
            return assemblySerializers;
        }
    }

    private static void RegisterSerializers(AssemblySerializers assemblySerializers)
    {
        // Register data contract aliases
        foreach (var dataContractAliasEntry in assemblySerializers.DataContractAliases)
        {
            try
            {
                // TODO: Warning, exception or override if collision? (currently exception)
                DataContractAliasMapping.Add(dataContractAliasEntry.Name, dataContractAliasEntry.Type);
            }
            catch (Exception)
            {
                throw new InvalidOperationException($"Two different classes have the same DataContract Alias [{dataContractAliasEntry.Name}]: {dataContractAliasEntry.Type} and {DataContractAliasMapping[dataContractAliasEntry.Name]}");
            }
        }

        // Register serializers
        foreach (var assemblySerializerPerProfile in assemblySerializers.Profiles)
        {
            var profile = assemblySerializerPerProfile.Key;

            if (!DataSerializersPerProfile.TryGetValue(profile, out var dataSerializers))
            {
                dataSerializers = new Dictionary<Type, AssemblySerializerEntry>();
                DataSerializersPerProfile.Add(profile, dataSerializers);
            }

            foreach (var assemblySerializer in assemblySerializerPerProfile.Value)
            {
                dataSerializers.TryAdd(assemblySerializer.ObjectType, assemblySerializer);
            }

View on GitHub (pinned to 96fad776d2)