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
- Search for all factories whose ConfigType matches the one in the message and ensure only one exists per group.
- If multiple variants are intended, give each a distinct ConfigGroup name.
- 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
- Grep for all factories with the same ConfigType; keep exactly one per group.
- Use distinct ConfigGroup names when multiple variants are intentional.
- Add a startup test that fails on duplicate registrations.
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
- config factory not found: {configType.FullName}
- config factory target is null: {factory.GetType().FullName}
- config group is empty: {factory.GetType().FullName}
- create config factory failed: {factoryType.FullName}
- config create failed: {configType.FullName}
AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13).
Data as JSON: /api/errors/4151a84da62ba525.
Report an issue: GitHub.