alibaba/nacos · error · IllegalArgumentException

Runtime Version range must be canonical

Error message

Runtime Version range must be canonical

What it means

Thrown by validateRuntimeVersionBinding when the versionRange string, after parsing and canonicalization via AgentVersionRange.parse, does not equal the raw input. This enforces canonical form so that range identity and duplicate detection are stable. AgentVersionRange canonicalizes by reformatting versions (via AgentVersion.toString) and by collapsing a single-point closed interval '[a,a]' or '[x,x]' into the exact form '[x]'. Any non-canonical spelling — extra whitespace, non-canonical version text, or a redundant closed range around identical bounds — is rejected.

Source

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

        }
        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");
    }
    
    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()) {

View on GitHub (pinned to 9b989acdf1)

Solutions

  1. Run AgentVersionRange.parse(range).getValue() (or AgentValidationUtils.validateVersionRange after fixing) and store the returned canonical string as versionRange.
  2. For a single version use the exact form '[1.0.0]' (closed brackets, no comma).
  3. For an interval use '[lo,hi]', '[lo,hi)', '(lo,hi]', or '(lo,hi)' with canonical version spellings and no surrounding whitespace.
  4. Leave a bound empty only with the open delimiter on that side, e.g. '[1.0.0,)'.
  5. If you only need one version, prefer setting both runtimeVersion and versionRange to the canonical exact form.

Example fix

// before
b.setVersionRange("[ 1.0.0, 2.0.0 )");  // whitespace -> rejected
b.setVersionRange("[1.0.0,1.0.0]");      // collapses -> rejected
// after
b.setVersionRange("[1.0.0,2.0.0)");
// single version:
b.setVersionRange("[1.0.0]");
Defensive patterns

Strategy: validation

Validate before calling

import com.alibaba.nacos.api.ai.utils.AgentValidationUtils;
// AgentVersionRange is package-private; use the public validator to confirm parse,
// then canonicalize via the same parse path exposed indirectly:
AgentValidationUtils.validateVersionRange(range); // throws if unparseable
// To get canonical form, mirror the rule: trim, no internal whitespace,
// single-point '[v,v]' -> '[v]'.

Type guard

// canonical check without package-private access: round-trip equality
static boolean isCanonicalRange(String range) {
    String trimmed = range.trim();
    if (!trimmed.equals(range)) return false;
    // no whitespace inside brackets
    if (range.matches(".*\\s.*")) return false;
    return true;
}

Try / catch

try {
    AgentModelValidator.validateRuntimeEndpointSnapshot(snapshot);
} catch (IllegalArgumentException e) {
    if (e.getMessage().equals("Runtime Version range must be canonical")) {
        // re-emit versionRange from AgentVersionRange.parse(range).getValue()
    } else throw e;
}

Prevention

When it happens

Trigger: A RuntimeVersionBinding.versionRange that parses but is not in canonical form. Examples that trigger: '[ 1.0.0,2.0.0 ]' (whitespace), '[1.0.0.0,2.0.0)' where AgentVersion normalizes '1.0.0.0', '[1.0.0,1.0.0]' (collapses to '[1.0.0]'). Reached during validateRuntimeEndpointSnapshotItem -> validateRuntimeVersionBinding in the runtime snapshot push path.

Common situations: Hand-writing version ranges with spaces around bounds; pasting SemVer-style pre-release text that AgentVersion rewrites; using a closed range '[v,v]' for a single version instead of the canonical exact form '[v]'; building range strings from non-canonical version sources.

Related errors


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