egametang/ET · error

duplicate config factory: {factory.ConfigType.FullName} grou

Error message

duplicate config factory: {factory.ConfigType.FullName} group={configGroup}

What it means

Thrown by ConfigFactoryGroupCollection.Add when TryAdd(configGroup, factory) fails — i.e. a factory for the SAME (ConfigType, configGroup) pair was already registered. The collection keys factories by ConfigType then by group name, so a duplicate means two factories compete to produce the same config category under the same group.

Source

Thrown at Packages/cn.etetet.loader/Scripts/Loader/Share/ConfigLoaderHelper.cs:31

            if (factory.ConfigType == null)
            {
                throw new Exception($"config factory target is null: {factory.GetType().FullName}");
            }

            if (string.IsNullOrWhiteSpace(configGroup))
            {
                throw new Exception($"config group is empty: {factory.GetType().FullName}");
            }

            if (!this.factories.TryGetValue(factory.ConfigType, out Dictionary<string, IConfigFactory> groupFactories))
            {
                groupFactories = new Dictionary<string, IConfigFactory>(StringComparer.Ordinal);
                this.factories.Add(factory.ConfigType, groupFactories);
            }

            if (!groupFactories.TryAdd(configGroup, factory))
            {
                throw new Exception($"duplicate config factory: {factory.ConfigType.FullName} group={configGroup}");
            }
        }

        public bool TryGetFactories(Type configType, out Dictionary<string, IConfigFactory> groupFactories)
        {
            return this.factories.TryGetValue(configType, out groupFactories);
        }
    }

    internal static class ConfigLoaderHelper
    {
        public static List<Type> GetConfigTypes()
        {
            List<Type> configTypes = new(CodeTypes.Instance.GetTypes(typeof(ConfigProcessAttribute)));
            configTypes.Sort((left, right) => string.CompareOrdinal(left.FullName, right.FullName));
            return configTypes;
        }

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Search for all factories whose ConfigType matches the one in the message and ensure only one exists per group.
  2. If multiple variants are intended, give each a distinct ConfigGroup name.
  3. Remove or rename the duplicate factory class.

Example fix

// before — two factories, both ConfigType=ItemConfigCategory, both default group
public class ItemConfigFactoryA : IConfigFactory { public Type ConfigType => typeof(ItemConfigCategory); }
public class ItemConfigFactoryB : IConfigFactory { public Type ConfigType => typeof(ItemConfigCategory); }

// after — only one, or distinct groups
public class ItemConfigFactory : IConfigFactory { public Type ConfigType => typeof(ItemConfigCategory); }
Defensive patterns

Strategy: validation

Validate before calling

// Before adding, check for an existing registration
if (groupFactories != null && groupFactories.ContainsKey(configGroup))
{ Log.Error($"duplicate factory for {factory.ConfigType} group={configGroup}"); return; }

Prevention

When it happens

Trigger: Two IConfigFactory classes both return the same ConfigType and the same ConfigGroup attribute/default. CreateConfigFactories iterates all factory types sorted by FullName and Add's each; the second registration of an identical (type, group) pair throws.

Common situations: A factory was copy-pasted without changing its target ConfigType; a codegen run emitted a duplicate; an old factory wasn't removed after adding a new one for the same category.

Related errors


AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13). Data as JSON: /api/errors/4151a84da62ba525. Report an issue: GitHub.