{"record":{"id":"85125fb1d0c6b71a","repo":"alibaba/nacos","slug":"duplicate-protocol-for-agent-version","errorCode":null,"errorMessage":"Duplicate protocol for Agent Version {}: {}","messagePattern":"Duplicate protocol for Agent Version (.+?): (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/com/alibaba/nacos/ai/service/agent/metadata/AgentVersionCatalogBuilder.java","lineNumber":124,"sourceCode":"    private static Map<String, List<String>> validateAndCopyProtocols(\n        Map<String, List<String>> onlineVersionProtocols) {\n        Map<String, List<String>> result = new LinkedHashMap<String, List<String>>();\n        for (Map.Entry<String, List<String>> entry : onlineVersionProtocols.entrySet()) {\n            String version = entry.getKey();\n            AgentValidationUtils.validateVersion(version);\n            List<String> protocols = entry.getValue();\n            if (protocols == null || protocols.isEmpty()\n                || protocols.size() > MAX_PROTOCOLS_PER_VERSION) {\n                throw new IllegalArgumentException(\n                    \"Online Agent Version protocols must contain 1 to \"\n                        + MAX_PROTOCOLS_PER_VERSION + \" values\");\n            }\n            Set<String> uniqueProtocols = new HashSet<String>();\n            List<String> protocolCopy = new ArrayList<String>(protocols.size());\n            for (String protocol : protocols) {\n                AgentValidationUtils.validateProtocol(protocol);\n                if (!uniqueProtocols.add(protocol)) {\n                    throw new IllegalArgumentException(\n                        \"Duplicate protocol for Agent Version \" + version + \": \" + protocol);\n                }\n                protocolCopy.add(protocol);\n            }\n            result.put(version, protocolCopy);\n        }\n        return result;\n    }\n    \n    private static Map<String, String> validateAndSortLabels(Map<String, String> labels) {\n        for (Map.Entry<String, String> entry : labels.entrySet()) {\n            AgentValidationUtils.validateLabel(entry.getKey());\n            AgentValidationUtils.validateVersion(entry.getValue());\n        }\n        return sortLabels(labels);\n    }\n    \n    private static Map<String, String> sortLabels(Map<String, String> labels) {","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/ai/src/main/java/com/alibaba/nacos/ai/service/agent/metadata/AgentVersionCatalogBuilder.java#L106-L142","documentation":"Each online Agent Version must declare a unique set of protocols — no protocol string may appear twice for the same version. The AgentVersionCatalogBuilder.validateAndCopyProtocols method (line 123) throws this when a duplicate protocol is detected while building the unique-protocol copy. The error message includes the version and the duplicated protocol string.","triggerScenarios":"Calling AgentVersionCatalogBuilder.build() where a version's protocol list contains the same string twice, e.g. [\"a2a\",\"a2a\"] or [\"mcp\",\"MCP\"] (case-sensitive, so \"mcp\" and \"MCP\" are distinct). This is an internal server-side validation.","commonSituations":"Client registration code that concatenates protocol lists from multiple sources without deduplication; merging configurations that both specify the same protocol; copy-paste errors in protocol list construction.","solutions":["Deduplicate the protocol list before registering: new LinkedHashSet<>(protocols) then new ArrayList<>(set).","Audit the client code that assembles the protocols list to ensure each protocol is added at most once.","Log the final protocol list before submission to catch duplicates during development."],"exampleFix":"// before\nList<String> protocols = Arrays.asList(\"a2a\", \"mcp\", \"a2a\");\n// after\nList<String> protocols = new ArrayList<>(new LinkedHashSet<>(Arrays.asList(\"a2a\", \"mcp\", \"a2a\")));","handlingStrategy":"validation","validationCode":"for (Map.Entry<String,List<String>> entry : onlineVersionProtocols.entrySet()) {\n    Set<String> seen = new HashSet<>();\n    for (String p : entry.getValue()) {\n        if (!seen.add(p)) {\n            throw new IllegalArgumentException(\"Duplicate protocol for \" + entry.getKey() + \": \" + p);\n        }\n    }\n}","typeGuard":"public static boolean hasUniqueProtocols(Map<String,List<String>> map) {\n    for (Map.Entry<String,List<String>> entry : map.entrySet()) {\n        if (new HashSet<>(entry.getValue()).size() != entry.getValue().size()) return false;\n    }\n    return true;\n}","tryCatchPattern":"try {\n    AgentVersionCatalogBuilder.build(onlineVersionProtocols, labels);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Duplicate protocol\")) {\n        // deduplicate: onlineVersionProtocols.put(v, new ArrayList<>(new LinkedHashSet<>(list)))\n    }\n    throw e;\n}","preventionTips":["Deduplicate protocol lists with LinkedHashSet before submitting registrations.","Use Set<String> in client-side DTOs to prevent duplicates at the source.","Log final protocol lists before build() during development."],"tags":["validation","ai-agent","version-catalog","protocols","deduplication"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}