{"record":{"id":"1bbf29ae1551e7b4","repo":"dotnet/yarp","slug":"more-than-one-typeof-t-found-with-the-same-iden","errorCode":null,"errorMessage":"More than one {typeof(T)} found with the same identifier.","messagePattern":"More than one (.+?) found with the same identifier\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/ReverseProxy/Utilities/ServiceLookupHelper.cs","lineNumber":22,"sourceCode":"using System;\nusing System.Collections.Frozen;\nusing System.Collections.Generic;\n\nnamespace Yarp.ReverseProxy.Utilities;\n\ninternal static class ServiceLookupHelper\n{\n    public static FrozenDictionary<string, T> ToDictionaryByUniqueId<T>(this IEnumerable<T> services, Func<T, string> idSelector)\n    {\n        ArgumentNullException.ThrowIfNull(services);\n\n        var result = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);\n\n        foreach (var service in services)\n        {\n            if (!result.TryAdd(idSelector(service), service))\n            {\n                throw new ArgumentException($\"More than one {typeof(T)} found with the same identifier.\", nameof(services));\n            }\n        }\n\n        return result.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);\n    }\n\n    public static T GetRequiredServiceById<T>(this FrozenDictionary<string, T> services, string? id, string defaultId)\n    {\n        var lookup = id;\n        if (string.IsNullOrEmpty(lookup))\n        {\n            lookup = defaultId;\n        }\n\n        if (!services.TryGetValue(lookup, out var result))\n        {\n            throw new ArgumentException($\"No {typeof(T)} was found for the id '{lookup}'.\", nameof(id));\n        }","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/dotnet/yarp/blob/bd11867bee7df522e7fd3effb08a9c85fd616908/src/ReverseProxy/Utilities/ServiceLookupHelper.cs#L4-L40","documentation":"Thrown by ServiceLookupHelper.ToDictionaryByUniqueId while building the case-insensitive name->policy lookup tables that YARP's middleware and validators use (active/passive health checks, load balancing, session affinity, destination policies). It fires the moment two registered policy instances return the same Name, because the dictionary cannot hold both. The collision is detected once at construction/startup, so it surfaces during DI resolution of the first middleware/validator that consumes that policy category.","triggerScenarios":"Registering two IActiveHealthCheckPolicy/IPassiveHealthCheckPolicy/ILoadBalancingPolicy/ISessionAffinityPolicy/IAffinityFailurePolicy/IDestinationPolicy implementations whose Name properties evaluate equal (OrdinalIgnoreCase). For example a custom ILoadBalancingPolicy returning LoadBalancingPolicies.RoundRobin while the built-in RoundRobinPolicy is also registered, or two custom policies with identical Name strings.","commonSituations":"Naming a custom policy with a string that collides with a YARP built-in constant (e.g. \"PowerOfTwoChoices\", \"ConsecutiveFailures\", \"TransportFailureRate\", \"HashCookie\"); copy-pasting a policy class and forgetting to change its Name; accidentally registering the same policy singleton twice through different Add* calls; a Name property that returns an empty/whitespace string so every such policy collides on \"\".","solutions":["Give each custom policy a unique Name value and verify it against the built-in constants in LoadBalancingPolicies, HealthCheckConstants, SessionAffinityConstants.","Search the DI registrations and confirm no policy is added twice (e.g. both via AddLoadBalancingPolicies and a manual AddSingleton<ILoadBalancingPolicy>).","Ensure Name never returns null/empty; if it can, fix the property to return a stable non-empty identifier.","Run with a logger attached at startup to see typeof(T) in the message, which identifies the exact policy category in conflict."],"exampleFix":"// before\npublic string Name => \"RoundRobin\"; // collides with built-in\n\n// after\npublic string Name => \"MyRoundRobin\";","handlingStrategy":"validation","validationCode":"// Before registering policies, assert all Names are unique (OrdinalIgnoreCase).\nvar policies = builder.Services.BuildServiceProvider()\n    .GetServices<ILoadBalancingPolicy>().ToList(); // repeat per policy category\nvar dupes = policies.GroupBy(p => p.Name, StringComparer.OrdinalIgnoreCase)\n    .Where(g => g.Count() > 1).Select(g => g.Key).ToList();\nif (dupes.Count > 0)\n    throw new InvalidOperationException(\"Duplicate policy names: \" + string.Join(\", \", dupes));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Centralize policy Name constants in static classes and reference them everywhere instead of string literals.","Write a startup health check that resolves each policy category dictionary and fails fast on duplicates.","Never register the same policy through two different Add* paths; pick one registration site.","Ensure every policy Name property returns a non-empty, stable value."],"tags":["configuration","startup","di-registration","duplicate-policy","yarp"],"backgroundTag":null,"analyzedSha":"bd11867bee7df522e7fd3effb08a9c85fd616908","analyzedAt":"2026-08-13T21:29:49.359Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}