{"record":{"id":"b0de93c3de3e11ac","repo":"alibaba/nacos","slug":"duplicate-runtime-version-binding","errorCode":null,"errorMessage":"Duplicate Runtime Version binding","messagePattern":"Duplicate Runtime Version binding","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/com/alibaba/nacos/api/ai/utils/AgentModelValidator.java","lineNumber":596,"sourceCode":"    private static void validateRuntimeVersionBinding(RuntimeVersionBinding binding,\n        AgentVersion selectedVersion, Set<String> bindingKeys) {\n        requireNonNull(binding, \"runtime Version binding\");\n        AgentVersion runtimeVersion = AgentVersion.parse(binding.getRuntimeVersion());\n        AgentVersionRange versionRange = AgentVersionRange.parse(binding.getVersionRange());\n        if (!versionRange.getValue().equals(binding.getVersionRange())) {\n            throw new IllegalArgumentException(\"Runtime Version range must be canonical\");\n        }\n        if (!versionRange.contains(runtimeVersion)) {\n            throw new IllegalArgumentException(\n                \"versionRange must contain runtimeVersion: \" + runtimeVersion);\n        }\n        if (selectedVersion != null && !versionRange.contains(selectedVersion)) {\n            throw new IllegalArgumentException(\n                \"Snapshot binding does not match selected Version: \" + selectedVersion);\n        }\n        String bindingKey = runtimeVersion + \"\\u0000\" + versionRange.getValue();\n        if (!bindingKeys.add(bindingKey)) {\n            throw new IllegalArgumentException(\"Duplicate Runtime Version binding\");\n        }\n    }\n    \n    private static void validateRuntimeEndpointState(RuntimeEndpointSnapshotItem item) {\n        RuntimeEndpointState expected;\n        if (!item.getEnabled()) {\n            expected = RuntimeEndpointState.DISABLED;\n        } else if (!item.getHealthy()) {\n            expected = RuntimeEndpointState.UNHEALTHY;\n        } else {\n            expected = RuntimeEndpointState.AVAILABLE;\n        }\n        if (item.getState() != expected) {\n            throw new IllegalArgumentException(\n                \"Runtime Endpoint state must be \" + expected.name());\n        }\n    }\n    ","sourceCodeStart":578,"sourceCodeEnd":614,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/api/src/main/java/com/alibaba/nacos/api/ai/utils/AgentModelValidator.java#L578-L614","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Dedup the bindings list by the (runtimeVersion, canonical versionRange) pair before submitting.","If two bindings look identical, merge them — they convey no additional information.","Build bindings from a Map keyed by runtimeVersion + AgentVersionRange.parse(range).getValue() to prevent insertion of duplicates.","Re-emit the binding list from a single source of truth rather than concatenating partial lists."],"exampleFix":"// before\nitem.setBindings(List.of(binding(\"1.0.0\",\"[1.0.0,2.0.0)\"),\n                      binding(\"1.0.0\",\"[1.0.0,2.0.0)\"))); // duplicate\n// after\nitem.setBindings(List.of(binding(\"1.0.0\",\"[1.0.0,2.0.0)\")));\n// add another only if runtimeVersion or canonical range differs:\n//   binding(\"1.5.0\",\"[1.0.0,2.0.0)\")","handlingStrategy":"validation","validationCode":"import java.util.HashSet;\nimport java.util.Set;\n\nfor (RuntimeEndpointSnapshotItem it : snapshot.getItems()) {\n    Set<String> keys = new HashSet<>();\n    for (RuntimeVersionBinding b : it.getBindings()) {\n        // canonical key: runtimeVersion + '\\u0000' + canonicalVersionRange\n        String key = b.getRuntimeVersion() + \"\\u0000\" + b.getVersionRange();\n        if (!keys.add(key)) {\n            throw new IllegalStateException(\"duplicate binding: \" + key);\n        }\n    }\n}","typeGuard":"static boolean noDuplicateBindings(RuntimeEndpointSnapshotItem it) {\n    Set<String> s = new HashSet<>();\n    for (RuntimeVersionBinding b : it.getBindings()) {\n        if (!s.add(b.getRuntimeVersion() + \"\\u0000\" + b.getVersionRange())) return false;\n    }\n    return true;\n}","tryCatchPattern":"try {\n    AgentModelValidator.validateRuntimeEndpointSnapshot(snapshot);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().equals(\"Duplicate Runtime Version binding\")) {\n        // dedup the item's bindings by (runtimeVersion, versionRange)\n    } else throw e;\n}","preventionTips":["Build bindings from a Map keyed by runtimeVersion+canonicalRange.","Canonicalize range strings before keying to avoid equivalent-but-non-identical duplicates.","Merge identical bindings rather than emitting both."],"tags":["ai-agent","validation","duplicate","runtime-snapshot","bindings"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}