alibaba/nacos · error · IllegalArgumentException

Duplicate runtime Endpoint

Error message

Duplicate runtime Endpoint: {endpointKey}

What it means

Thrown while validating a runtime Endpoint snapshot (RuntimeEndpointSnapshot) when two snapshot items resolve to the same EndpointNaturalKey across the whole snapshot. Unlike declared endpoints (per-CallInterface), runtime uniqueness is enforced across all items of one snapshot. The key is (namespaceId, agentName, protocol, normalizedHost, effectivePort, transport), so URIs that differ only cosmetically collide.

Solutions

  1. Dedup snapshot items by the canonical (host, port, transport) tuple; the message prints the colliding key as namespaceId/agentName/protocol/host:port/transport.
  2. Merge health, bindings, enabled, and state into a single RuntimeEndpointSnapshotItem for that address rather than emitting two.
  3. If you need the same host on two distinct transports (e.g. grpc vs http), ensure the transport field differs — that is part of the natural key.
  4. Pre-compute EndpointNaturalKey.of(...) for each item client-side to detect collisions before push.

Example fix

// before
itemA.setEndpoint(uri("https://svc:443")); itemA.setState(AVAILABLE);
itemB.setEndpoint(uri("https://svc:443")); itemB.setState(DISABLED); // duplicate key
// after
itemA.setEndpoint(uri("https://svc:443")); // single item, pick one authoritative state
Defensive patterns

Strategy: validation

Validate before calling

import com.alibaba.nacos.api.ai.utils.EndpointNaturalKey;
import java.util.HashSet;
import java.util.Set;

Set<EndpointNaturalKey> seen = new HashSet<>();
for (RuntimeEndpointSnapshotItem it : snapshot.getItems()) {
    EndpointNaturalKey k = EndpointNaturalKey.of(
        snapshot.getNamespaceId(), snapshot.getAgentName(),
        snapshot.getProtocol(), it.getEndpoint());
    if (!seen.add(k)) {
        throw new IllegalStateException("duplicate runtime endpoint: " + k);
    }
}

Type guard

static boolean noRuntimeDuplicates(RuntimeEndpointSnapshot snap) {
    Set<EndpointNaturalKey> s = new HashSet<>();
    for (RuntimeEndpointSnapshotItem it : snap.getItems()) {
        if (!s.add(EndpointNaturalKey.of(snap.getNamespaceId(), snap.getAgentName(),
                snap.getProtocol(), it.getEndpoint()))) return false;
    }
    return true;
}

Try / catch

try {
    AgentModelValidator.validateRuntimeEndpointSnapshot(snapshot);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Duplicate runtime Endpoint")) {
        // merge the two items' health/bindings/state into one
    } else throw e;
}

Prevention

When it happens

Trigger: validateRuntimeEndpointSnapshot iterates snapshot.getItems(), passing one shared endpointKeys set into validateRuntimeEndpointSnapshotItem, which calls EndpointNaturalKey.of(snapshot.getNamespaceId(), snapshot.getAgentName(), snapshot.getProtocol(), item.getEndpoint()). The second item with an already-seen key triggers this. Reached via AgentModelValidator.validateRuntimeEndpointSnapshot, typically invoked by the runtime management / RAD (Remote Agent Discovery) push path.

Common situations: A runtime agent reporting the same backend address twice with different health/bindings/state (those do not affect identity); an aggregator merging snapshots from two sub-agents without dedup; URI case/default-port collisions such as 'https://H' and 'https://h:443'; switching a host between IPv4 and a hostname that resolve to the same effective address but you intended distinct entries.

Related errors


AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14). Data as JSON: /api/errors/2660b523351ec8a1. Report an issue: GitHub.

Appendix: source

Thrown at api/src/main/java/com/alibaba/nacos/api/ai/utils/AgentModelValidator.java:559

        for (Endpoint endpoint : endpoints) {
            validateEndpoint(endpoint);
            EndpointNaturalKey key = EndpointNaturalKey.of(namespaceId, agentName, protocol,
                endpoint);
            if (!endpointKeys.add(key)) {
                throw new IllegalArgumentException("Duplicate declared Endpoint: " + key);
            }
        }
    }
    
    private static void validateRuntimeEndpointSnapshotItem(RuntimeEndpointSnapshot snapshot,
        AgentVersion selectedVersion, RuntimeEndpointSnapshotItem item,
        Set<EndpointNaturalKey> endpointKeys) {
        requireNonNull(item, "runtimeEndpointSnapshot item");
        validateEndpoint(item.getEndpoint());
        EndpointNaturalKey endpointKey = EndpointNaturalKey.of(snapshot.getNamespaceId(),
            snapshot.getAgentName(), snapshot.getProtocol(), item.getEndpoint());
        if (!endpointKeys.add(endpointKey)) {
            throw new IllegalArgumentException("Duplicate runtime Endpoint: " + endpointKey);
        }
        
        List<RuntimeVersionBinding> bindings = item.getBindings();
        requireNonNull(bindings, "runtimeEndpointSnapshot.bindings");
        if (bindings.isEmpty()) {
            throw new IllegalArgumentException("Runtime Endpoint bindings must not be empty");
        }
        Set<String> bindingKeys = new HashSet<String>();
        for (RuntimeVersionBinding binding : bindings) {
            validateRuntimeVersionBinding(binding, selectedVersion, bindingKeys);
        }
        requireNonNull(item.getState(), "runtime Endpoint state");
        requireNonNull(item.getEnabled(), "runtime Endpoint enabled");
        requireNonNull(item.getHealthy(), "runtime Endpoint healthy");
        validateRuntimeEndpointState(item);
        validateEpochMillis(item.getLastUpdatedTime(), "lastUpdatedTime");
    }
    

View on GitHub (pinned to 9b989acdf1)