{"record":{"id":"88e2a81258a8516c","repo":"alibaba/nacos","slug":"protocol-must-be-unique-callinterface-protocol-88e2a8","errorCode":null,"errorMessage":"protocol must be unique: ${callInterface.protocol}","messagePattern":"protocol must be unique: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"console-ui-next/src/pages/newAgent/agent-console-model.ts","lineNumber":354,"sourceCode":"    }\n    seen.add(key);\n    return [{ uri, transport }];\n  });\n  return {\n    protocol: required(editor.customProtocol, 'protocol'),\n    protocolVersion: editor.customProtocolVersion.trim() || undefined,\n    descriptorMediaType: required(editor.customDescriptorMediaType, 'descriptorMediaType'),\n    nativeDescriptor,\n    endpointSourceOrder: endpointSourceOrder(editor.endpointSourceMode),\n    declaredEndpoints: declaredEndpoints.length > 0 ? declaredEndpoints : undefined,\n  };\n}\n\nfunction validateUniqueProtocols(callInterfaces: AgentCallInterface[]): AgentCallInterface[] {\n  const protocols = new Set<string>();\n  for (const callInterface of callInterfaces) {\n    if (protocols.has(callInterface.protocol)) {\n      throw new Error(`protocol must be unique: ${callInterface.protocol}`);\n    }\n    protocols.add(callInterface.protocol);\n  }\n  return callInterfaces;\n}\n\nfunction serializeOptionalObject(value: string, name: string): string | undefined {\n  if (!value.trim()) {\n    return undefined;\n  }\n  const parsed = parseJson(value, name);\n  if (parsed === null || Array.isArray(parsed) || typeof parsed !== 'object') {\n    throw new Error(`${name} must be a JSON object`);\n  }\n  return JSON.stringify(parsed);\n}\n\nfunction serializeCallInterfaces(","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/console-ui-next/src/pages/newAgent/agent-console-model.ts#L336-L372","documentation":"Thrown by validateUniqueProtocols when two callInterfaces share the same `protocol` value. Each protocol type (e.g. 'A2A', 'GRPC') may appear only once per agent; a duplicate is ambiguous for routing. The set tracks seen protocols and rejects the second occurrence.","triggerScenarios":"User adds two structured protocol editors both configured as 'A2A', or duplicates a 'GRPC' entry. The protocolEditorKind list yields two interfaces with the same protocol string.","commonSituations":"Copy-pasting a protocol editor block to create a second endpoint but forgetting to change the protocol. Bulk import producing duplicates.","solutions":["Ensure each callInterface has a distinct protocol value.","If you need two endpoints for the same protocol, merge them into one interface with multiple declaredEndpoints.","Dedupe the protocolEditors array before serializeCallInterfaces."],"exampleFix":"// before\n[{protocol:'A2A',...},{protocol:'A2A',...}]\n\n// after\n[{protocol:'A2A', declaredEndpoints:[{uri:'a',transport:'GRPC'},{uri:'b',transport:'GRPC'}]}]","handlingStrategy":"validation","validationCode":"function dedupeProtocols(list) {\n  const seen = new Set();\n  for (const it of list) { if (seen.has(it.protocol)) throw new Error(`Duplicate protocol: ${it.protocol}`); seen.add(it.protocol); }\n}","typeGuard":"function hasUniqueProtocols(list: { protocol: string }[]): boolean {\n  return new Set(list.map((i) => i.protocol)).size === list.length;\n}","tryCatchPattern":"try { validateUniqueProtocols(list); } catch (e) {\n  if (/protocol must be unique/.test(e.message)) { toast.error('Each protocol may appear only once'); }\n}","preventionTips":["Dedupe protocols before submit","Merge same-protocol endpoints into one interface","Show duplicate warnings in the UI"],"tags":["validation","protocol","uniqueness","agent"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}