alibaba/nacos · error · IllegalArgumentException

Duplicate Runtime Version binding

Error message

Duplicate Runtime Version binding

What it means

Thrown by validateRuntimeVersionBinding when two bindings within the SAME RuntimeEndpointSnapshotItem share the same (runtimeVersion, canonical versionRange) pair. The validator builds a composite key runtimeVersion + '\u0000' + versionRange.getValue() per binding and rejects the second collision. Only the version identity matters — duplicate bindings that differ only by object identity but not by these two fields are treated as duplicates.

Source

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

    private static void validateRuntimeVersionBinding(RuntimeVersionBinding binding,
        AgentVersion selectedVersion, Set<String> bindingKeys) {
        requireNonNull(binding, "runtime Version binding");
        AgentVersion runtimeVersion = AgentVersion.parse(binding.getRuntimeVersion());
        AgentVersionRange versionRange = AgentVersionRange.parse(binding.getVersionRange());
        if (!versionRange.getValue().equals(binding.getVersionRange())) {
            throw new IllegalArgumentException("Runtime Version range must be canonical");
        }
        if (!versionRange.contains(runtimeVersion)) {
            throw new IllegalArgumentException(
                "versionRange must contain runtimeVersion: " + runtimeVersion);
        }
        if (selectedVersion != null && !versionRange.contains(selectedVersion)) {
            throw new IllegalArgumentException(
                "Snapshot binding does not match selected Version: " + selectedVersion);
        }
        String bindingKey = runtimeVersion + "\u0000" + versionRange.getValue();
        if (!bindingKeys.add(bindingKey)) {
            throw new IllegalArgumentException("Duplicate Runtime Version binding");
        }
    }
    
    private static void validateRuntimeEndpointState(RuntimeEndpointSnapshotItem item) {
        RuntimeEndpointState expected;
        if (!item.getEnabled()) {
            expected = RuntimeEndpointState.DISABLED;
        } else if (!item.getHealthy()) {
            expected = RuntimeEndpointState.UNHEALTHY;
        } else {
            expected = RuntimeEndpointState.AVAILABLE;
        }
        if (item.getState() != expected) {
            throw new IllegalArgumentException(
                "Runtime Endpoint state must be " + expected.name());
        }
    }
    

View on GitHub (pinned to 9b989acdf1)

Solutions

  1. Dedup the bindings list by the (runtimeVersion, canonical versionRange) pair before submitting.
  2. If two bindings look identical, merge them — they convey no additional information.
  3. Build bindings from a Map keyed by runtimeVersion + AgentVersionRange.parse(range).getValue() to prevent insertion of duplicates.
  4. Re-emit the binding list from a single source of truth rather than concatenating partial lists.

Example fix

// before
item.setBindings(List.of(binding("1.0.0","[1.0.0,2.0.0)"),
                      binding("1.0.0","[1.0.0,2.0.0)"))); // duplicate
// after
item.setBindings(List.of(binding("1.0.0","[1.0.0,2.0.0)")));
// add another only if runtimeVersion or canonical range differs:
//   binding("1.5.0","[1.0.0,2.0.0)")
Defensive patterns

Strategy: validation

Validate before calling

import java.util.HashSet;
import java.util.Set;

for (RuntimeEndpointSnapshotItem it : snapshot.getItems()) {
    Set<String> keys = new HashSet<>();
    for (RuntimeVersionBinding b : it.getBindings()) {
        // canonical key: runtimeVersion + '\u0000' + canonicalVersionRange
        String key = b.getRuntimeVersion() + "\u0000" + b.getVersionRange();
        if (!keys.add(key)) {
            throw new IllegalStateException("duplicate binding: " + key);
        }
    }
}

Type guard

static boolean noDuplicateBindings(RuntimeEndpointSnapshotItem it) {
    Set<String> s = new HashSet<>();
    for (RuntimeVersionBinding b : it.getBindings()) {
        if (!s.add(b.getRuntimeVersion() + "\u0000" + b.getVersionRange())) return false;
    }
    return true;
}

Try / catch

try {
    AgentModelValidator.validateRuntimeEndpointSnapshot(snapshot);
} catch (IllegalArgumentException e) {
    if (e.getMessage().equals("Duplicate Runtime Version binding")) {
        // dedup the item's bindings by (runtimeVersion, versionRange)
    } else throw e;
}

Prevention

When it happens

Trigger: A RuntimeEndpointSnapshotItem.bindings list containing two RuntimeVersionBinding entries whose runtimeVersion and canonical versionRange strings are equal. E.g. two bindings both '1.0.0' + '[1.0.0,2.0.0)'. Reached during validateRuntimeEndpointSnapshotItem -> validateRuntimeVersionBinding in the runtime snapshot push path.

Common situations: Appending a binding twice when merging config sources; a templating loop that emits the same version twice; two non-canonical-but-equivalent ranges (e.g. '[1.0.0,1.0.0]' and '[1.0.0]') that canonicalize to the same value — note the canonical check (error 504) would fire first for non-canonical input, so here the inputs are already canonical yet textually identical.

Related errors


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