{"record":{"id":"233f600b007dfb69","repo":"alibaba/nacos","slug":"agent-draft-must-contain-either-callinterfaces-or-233f60","errorCode":null,"errorMessage":"Agent draft must contain either callInterfaces or basedOnVersion","messagePattern":"Agent draft must contain either callInterfaces or basedOnVersion","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/com/alibaba/nacos/api/ai/model/agent/AgentDraftCreateRequest.java","lineNumber":68,"sourceCode":"    \n    private List<AgentCallInterface> callInterfaces;\n    \n    private String author;\n    \n    private String changeDescription;\n    \n    private String basedOnVersion;\n    \n    /**\n     * Validate the draft identity and content source.\n     */\n    public void validate() {\n        AgentAdminRequestUtils.validateIdentity(agentName);\n        AgentAdminRequestUtils.validateVersion(version);\n        boolean directContent = callInterfaces != null;\n        boolean copiedContent = !AgentAdminRequestUtils.isBlank(basedOnVersion);\n        if (directContent == copiedContent) {\n            throw new IllegalArgumentException(\n                \"Agent draft must contain either callInterfaces or basedOnVersion\");\n        }\n        if (copiedContent) {\n            AgentAdminRequestUtils.validateVersion(basedOnVersion);\n        }\n    }\n    \n    public String getAgentName() {\n        return agentName;\n    }\n    \n    public void setAgentName(String agentName) {\n        this.agentName = agentName;\n    }\n    \n    public String getDisplayName() {\n        return displayName;\n    }","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/api/src/main/java/com/alibaba/nacos/api/ai/model/agent/AgentDraftCreateRequest.java#L50-L86","documentation":"AgentDraftCreateRequest.validate enforces an XOR constraint: exactly one of callInterfaces (direct content) or basedOnVersion (copy from an existing version) must be provided. Setting both or neither triggers this error. This prevents ambiguous draft creation where the content source is unclear.","triggerScenarios":"Calling draftCreate.validate() when both callInterfaces and basedOnVersion are populated, or when both are null/blank. The method checks directContent == copiedContent (true when both are true or both are false).","commonSituations":"A form or API payload sends both fields. A client defaults callInterfaces to an empty list (non-null) AND sets basedOnVersion, triggering the both-set branch. Forgetting to populate either field in a create request.","solutions":["Provide callInterfaces with at least one interface when creating a draft from scratch, and leave basedOnVersion null.","Provide basedOnVersion (an existing version string) when copying, and leave callInterfaces null.","Ensure your API client does not default callInterfaces to an empty list when it should be absent.","Add a pre-validation check in your controller/service layer to enforce the XOR before calling validate()."],"exampleFix":"// before\nrequest.setCallInterfaces(List.of(iface));\nrequest.setBasedOnVersion(\"1.0.0\"); // both set -> error\n\n// after -- direct content path\nrequest.setCallInterfaces(List.of(iface));\nrequest.setBasedOnVersion(null);\n\n// OR copy path\nrequest.setCallInterfaces(null);\nrequest.setBasedOnVersion(\"1.0.0\");","handlingStrategy":"validation","validationCode":"boolean hasInterfaces = request.getCallInterfaces() != null;\nboolean hasBasedOn = request.getBasedOnVersion() != null\n    && !request.getBasedOnVersion().trim().isEmpty();\nif (hasInterfaces == hasBasedOn) {\n    throw new IllegalArgumentException(\n        \"Provide exactly one of callInterfaces or basedOnVersion\");\n}","typeGuard":"public static boolean isDraftCreateValid(AgentDraftCreateRequest req) {\n    boolean d = req.getCallInterfaces() != null;\n    boolean c = req.getBasedOnVersion() != null && !req.getBasedOnVersion().isBlank();\n    return d ^ c;\n}","tryCatchPattern":"try {\n    request.validate();\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"either callInterfaces or basedOnVersion\")) {\n        return badRequest(\"Specify exactly one content source for the draft\");\n    }\n    throw e;\n}","preventionTips":["Ensure your API client sends exactly one content source field.","Do not default callInterfaces to an empty list when using basedOnVersion.","Add XOR validation at the controller layer before calling validate()."],"tags":["agent-admin","validation","draft","xor-constraint"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}