{"record":{"id":"cfade3b7d9af1a87","repo":"alibaba/nacos","slug":"agent-version-must-not-contain-read-only-projectio","errorCode":null,"errorMessage":"Agent Version must not contain read-only projection fields","messagePattern":"Agent Version must not contain read-only projection fields","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"ai/src/main/java/com/alibaba/nacos/ai/service/agent/AgentPersistenceService.java","lineNumber":1073,"sourceCode":"        }\n        AgentValidationUtils.validateVersion(version.getVersion());\n        if (version.getCallInterfaces() == null) {\n            throw new IllegalArgumentException(\"Online Agent Version must contain callInterfaces\");\n        }\n        if (version.getNamespaceId() != null\n            && !namespaceId.equals(version.getNamespaceId())) {\n            throw new IllegalArgumentException(\"Agent Version namespaceId does not match request\");\n        }\n        if (version.getAgentName() != null && !agentName.equals(version.getAgentName())) {\n            throw new IllegalArgumentException(\"Agent Version agentName does not match request\");\n        }\n        if (version.getStatus() != null\n            && !AiConstants.Agent.VERSION_STATUS_ONLINE.equals(version.getStatus())) {\n            throw new IllegalArgumentException(\"Agent Version status must be online\");\n        }\n        if (version.getContentDigest() != null || version.getCreateTime() != null\n            || version.getUpdateTime() != null) {\n            throw new IllegalArgumentException(\n                \"Agent Version must not contain read-only projection fields\");\n        }\n        if (preferredLatest != null && !version.getVersion().equals(preferredLatest)) {\n            throw new IllegalArgumentException(\"preferredLatest must target the created Version\");\n        }\n    }\n    \n    private void validateAgentUpdateInputs(Agent replacement) {\n        if (replacement == null) {\n            throw new IllegalArgumentException(\"Agent replacement must not be null\");\n        }\n        AgentValidationUtils.validateNamespaceId(replacement.getNamespaceId());\n        AgentValidationUtils.validateAgentName(replacement.getAgentName());\n        if (replacement.getVersionInfo() != null || replacement.getVersionCatalog() != null\n            || replacement.getMetaVersion() != null || replacement.getCreateTime() != null\n            || replacement.getUpdateTime() != null) {\n            throw new IllegalArgumentException(\n                \"Agent update input must not contain read-only projection fields\");","sourceCodeStart":1055,"sourceCodeEnd":1091,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/ai/src/main/java/com/alibaba/nacos/ai/service/agent/AgentPersistenceService.java#L1055-L1091","documentation":"An AgentVersionDetail submitted for online creation must not include read-only server-managed projection fields: contentDigest, createTime, or updateTime. These are computed and assigned by the server during persistence; accepting client-supplied values would let callers forge digests or timestamps.","triggerScenarios":"Calling createOnlineVersion with an AgentVersionDetail where getContentDigest(), getCreateTime(), or getUpdateTime() returns a non-null value. This happens when a Version detail read from a GET response is reused for a create call without clearing these fields.","commonSituations":"A client fetches an existing Version, modifies callInterfaces, and re-submits it as a new online Version without nulling contentDigest/createTime/updateTime. An import tool preserves timestamps from the source system.","solutions":["Set version.setContentDigest(null), version.setCreateTime(null), version.setUpdateTime(null) before calling createOnlineVersion.","Build the Version detail from scratch using only writable fields rather than copying a read projection.","Use the toOnlineVersion(request) factory which omits all read-only fields."],"exampleFix":"// before — read-only fields carried over from a GET response\nversion.setContentDigest(\"sha256:abc...\");\nversion.setCreateTime(1700000000L);\npersistenceService.createOnlineVersion(ns, name, version, null); // throws\n\n// after — clear server-managed fields\nversion.setContentDigest(null);\nversion.setCreateTime(null);\nversion.setUpdateTime(null);\npersistenceService.createOnlineVersion(ns, name, version, null);","handlingStrategy":"validation","validationCode":"if (version.getContentDigest() != null || version.getCreateTime() != null || version.getUpdateTime() != null) {\n    throw new IllegalArgumentException(\"Version detail must not carry read-only projection fields\");\n}","typeGuard":"boolean hasNoProjectionFields(AgentVersionDetail v) {\n    return v.getContentDigest() == null && v.getCreateTime() == null && v.getUpdateTime() == null;\n}","tryCatchPattern":null,"preventionTips":["Never reuse a Version detail from a GET response for a create without clearing server fields.","Treat contentDigest, createTime, updateTime as strictly server-assigned.","Prefer toOnlineVersion(request) which never sets projection fields."],"tags":["agent","ai-registry","validation","read-only-fields","online-version"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}