alibaba/nacos · error · IllegalArgumentException
Duplicate runtime Endpoint: {endpointKey}
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.
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)
Solutions
- Dedup snapshot items by the canonical (host, port, transport) tuple; the message prints the colliding key as namespaceId/agentName/protocol/host:port/transport.
- Merge health, bindings, enabled, and state into a single RuntimeEndpointSnapshotItem for that address rather than emitting two.
- 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.
- 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
- Uniqueness spans the whole snapshot, not per item.
- Merge health/state/bindings for the same address instead of emitting two items.
- Same host:port but different transport IS allowed — set transport explicitly.
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
- Duplicate declared Endpoint: {key}
- Duplicate Runtime Version binding
- declaredEndpoints exceeds {MAX_DECLARED_ENDPOINTS} items
- Runtime Endpoint bindings must not be empty
- Runtime Version range must be canonical
AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14).
Data as JSON: /api/errors/2660b523351ec8a1.
Report an issue: GitHub.