{"record":{"id":"7ed554fe18b85c72","repo":"alibaba/nacos","slug":"agent-direct-online-request-must-contain-callinter","errorCode":null,"errorMessage":"Agent direct-online request must contain callInterfaces","messagePattern":"Agent direct-online request must contain callInterfaces","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"ai/src/main/java/com/alibaba/nacos/ai/service/agent/AgentOperationService.java","lineNumber":777,"sourceCode":"            AiConstants.Agent.VERSION_STATUS_ONLINE);\n        persistenceService.synchronizeDerivedState(namespaceId, agentName,\n            setAsLatest ? version : null, null,\n            AiConstants.Agent.VERSION_STATUS_DRAFT.equals(status) ? version : null,\n            AiConstants.Agent.VERSION_STATUS_REVIEWING.equals(status)\n                || AiConstants.Agent.VERSION_STATUS_REVIEWED.equals(status) ? version : null);\n        return persistenceService.getAgentVersion(namespaceId, agentName, version);\n    }\n    \n    private void validateDirectOnlineRequest(String namespaceId,\n        AgentDraftCreateRequest request) {\n        if (request == null) {\n            throw new IllegalArgumentException(\"Agent direct-online request must not be null\");\n        }\n        AgentValidationUtils.validateNamespaceId(namespaceId);\n        request.validate();\n        if (request.getCallInterfaces() == null || StringUtils.isNotBlank(\n            request.getBasedOnVersion())) {\n            throw new IllegalArgumentException(\n                \"Agent direct-online request must contain callInterfaces\");\n        }\n    }\n    \n    private AgentVersionDetail toOnlineVersion(AgentDraftCreateRequest request) {\n        AgentVersionDetail result = new AgentVersionDetail();\n        result.setVersion(request.getVersion());\n        result.setStatus(AiConstants.Agent.VERSION_STATUS_ONLINE);\n        result.setCallInterfaces(request.getCallInterfaces());\n        result.setAuthor(request.getAuthor());\n        result.setChangeDescription(request.getChangeDescription());\n        return result;\n    }\n    \n    private boolean containsProtocol(AgentVersionDetail version, String protocol) {\n        if (version.getCallInterfaces() == null) {\n            return false;\n        }","sourceCodeStart":759,"sourceCodeEnd":795,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/ai/src/main/java/com/alibaba/nacos/ai/service/agent/AgentOperationService.java#L759-L795","documentation":"Thrown by AgentOperationService.validateDirectOnlineRequest as an IllegalArgumentException when callInterfaces is null OR basedOnVersion is non-blank. A direct-online version must declare its own callInterfaces (it cannot derive from a base version), so the presence of basedOnVersion or absence of callInterfaces is rejected. This enforces that direct-online versions are self-contained.","triggerScenarios":"Calling the direct-online path with request.getCallInterfaces() == null. Calling it with a non-blank basedOnVersion (which implies deriving from another version, incompatible with direct-online semantics). Both conditions trigger the same exception.","commonSituations":"Client reuses a draft-create request (which has basedOnVersion set) for the direct-online path without clearing it. Frontend forgets to populate callInterfaces for direct-online. Misunderstanding that direct-online requires full interface declarations, not a delta from a base version.","solutions":["Ensure request.setCallInterfaces(...) is populated with a non-null list before the direct-online call.","Clear request.setBasedOnVersion(null) or empty string — direct-online does not derive from a base version.","Validate both conditions client-side before submitting: callInterfaces != null AND basedOnVersion is blank."],"exampleFix":"// before — reusing a draft request for direct-online\nAgentDraftCreateRequest req = new AgentDraftCreateRequest();\nreq.setBasedOnVersion(\"1.0.0\");\n// callInterfaces null\nservice.registerDirectOnline(ns, req);\n\n// after\nAgentDraftCreateRequest req = new AgentDraftCreateRequest();\nreq.setBasedOnVersion(null); // direct-online is self-contained\nreq.setCallInterfaces(List.of(new AgentCallInterface(\"A2A\", ...)));\nservice.registerDirectOnline(ns, req);","handlingStrategy":"validation","validationCode":"// Validate direct-online preconditions before calling\nif (request.getCallInterfaces() == null) {\n    throw new IllegalArgumentException(\"callInterfaces is required for direct-online\");\n}\nif (StringUtils.isNotBlank(request.getBasedOnVersion())) {\n    request.setBasedOnVersion(null); // direct-online is self-contained\n}\nagentOperationService.registerDirectOnline(ns, request);","typeGuard":"public static boolean isDirectOnlineReady(AgentDraftCreateRequest request) {\n    return request != null\n        && request.getCallInterfaces() != null\n        && !request.getCallInterfaces().isEmpty()\n        && StringUtils.isBlank(request.getBasedOnVersion());\n}","tryCatchPattern":"try {\n    agentOperationService.registerDirectOnline(ns, request);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"must contain callInterfaces\")) {\n        // fix the request and retry\n        request.setBasedOnVersion(null);\n        request.setCallInterfaces(loadCallInterfaces(request));\n        agentOperationService.registerDirectOnline(ns, request);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always populate callInterfaces with a non-null, non-empty list for direct-online.","Never set basedOnVersion on a direct-online request — it must be self-contained.","Use a dedicated request builder for direct-online that enforces both rules."],"tags":["agent","direct-online","illegal-argument","call-interfaces","validation","version"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}