dotnet/yarp · error · ArgumentException

More than one {typeof(T)} found with the same identifier.

Error message

More than one {typeof(T)} found with the same identifier.

What it means

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.

Source

Thrown at src/ReverseProxy/Utilities/ServiceLookupHelper.cs:22

using System;
using System.Collections.Frozen;
using System.Collections.Generic;

namespace Yarp.ReverseProxy.Utilities;

internal static class ServiceLookupHelper
{
    public static FrozenDictionary<string, T> ToDictionaryByUniqueId<T>(this IEnumerable<T> services, Func<T, string> idSelector)
    {
        ArgumentNullException.ThrowIfNull(services);

        var result = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);

        foreach (var service in services)
        {
            if (!result.TryAdd(idSelector(service), service))
            {
                throw new ArgumentException($"More than one {typeof(T)} found with the same identifier.", nameof(services));
            }
        }

        return result.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
    }

    public static T GetRequiredServiceById<T>(this FrozenDictionary<string, T> services, string? id, string defaultId)
    {
        var lookup = id;
        if (string.IsNullOrEmpty(lookup))
        {
            lookup = defaultId;
        }

        if (!services.TryGetValue(lookup, out var result))
        {
            throw new ArgumentException($"No {typeof(T)} was found for the id '{lookup}'.", nameof(id));
        }

View on GitHub (pinned to bd11867bee)

Solutions

  1. Give each custom policy a unique Name value and verify it against the built-in constants in LoadBalancingPolicies, HealthCheckConstants, SessionAffinityConstants.
  2. Search the DI registrations and confirm no policy is added twice (e.g. both via AddLoadBalancingPolicies and a manual AddSingleton<ILoadBalancingPolicy>).
  3. Ensure Name never returns null/empty; if it can, fix the property to return a stable non-empty identifier.
  4. Run with a logger attached at startup to see typeof(T) in the message, which identifies the exact policy category in conflict.

Example fix

// before
public string Name => "RoundRobin"; // collides with built-in

// after
public string Name => "MyRoundRobin";
Defensive patterns

Strategy: validation

Validate before calling

// Before registering policies, assert all Names are unique (OrdinalIgnoreCase).
var policies = builder.Services.BuildServiceProvider()
    .GetServices<ILoadBalancingPolicy>().ToList(); // repeat per policy category
var dupes = policies.GroupBy(p => p.Name, StringComparer.OrdinalIgnoreCase)
    .Where(g => g.Count() > 1).Select(g => g.Key).ToList();
if (dupes.Count > 0)
    throw new InvalidOperationException("Duplicate policy names: " + string.Join(", ", dupes));

Prevention

When it happens

Trigger: 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.

Common situations: 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 "".

Related errors


AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13). Data as JSON: /api/errors/1bbf29ae1551e7b4. Report an issue: GitHub.