{"record":{"id":"1057d773ed7dc085","repo":"alibaba/nacos","slug":"agent-update-input-must-not-contain-read-only-proj","errorCode":null,"errorMessage":"Agent update input must not contain read-only projection fields","messagePattern":"Agent update input 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":1090,"sourceCode":"            || 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\");\n        }\n    }\n    \n    private Agent normalizeAgentUpdate(Agent source, Agent current) {\n        Agent result = new Agent();\n        result.setNamespaceId(source.getNamespaceId());\n        result.setAgentName(source.getAgentName());\n        result.setDisplayName(source.getDisplayName());\n        result.setDescription(source.getDescription());\n        result.setIconUrl(source.getIconUrl());\n        result.setProvider(source.getProvider());\n        result.setTags(source.getTags());\n        result.setExtensions(source.getExtensions());\n        result.setStatus(source.getStatus());\n        result.setOwner(current.getOwner());\n        result.setScope(current.getScope());\n        result.setVersionInfo(current.getVersionInfo());","sourceCodeStart":1072,"sourceCodeEnd":1108,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/ai/src/main/java/com/alibaba/nacos/ai/service/agent/AgentPersistenceService.java#L1072-L1108","documentation":"An Agent replacement for metadata update must not include read-only server-managed projection fields: versionInfo, versionCatalog, metaVersion, createTime, or updateTime. These are derived and maintained by the server; accepting client values would corrupt version tracking and optimistic-concurrency semantics.","triggerScenarios":"Calling tryUpdateAgent with an Agent whose getVersionInfo(), getVersionCatalog(), getMetaVersion(), getCreateTime(), or getUpdateTime() returns non-null. This happens when a client fetches an Agent via GET and re-submits the full object as an update payload.","commonSituations":"A client GETs an Agent, modifies displayName, and PUTs the entire object back including versionInfo and timestamps. A generic client SDK serializes all fields on update. A migration tool copies full projections between environments.","solutions":["Build the update Agent from scratch with only writable fields (namespaceId, agentName, displayName, description, iconUrl, provider, tags, extensions, status, owner, scope).","Null out versionInfo, versionCatalog, metaVersion, createTime, updateTime on any Agent object before passing it to tryUpdateAgent.","Use a dedicated AgentUpdateRequest/Form that only exposes writable fields."],"exampleFix":"// before — full projection re-submitted as update\nAgent replacement = fetchedAgent; // from GET response\nreplacement.setDisplayName(\"New Name\");\npersistenceService.tryUpdateAgent(replacement, current); // throws\n\n// after — only writable fields\nAgent replacement = new Agent();\nreplacement.setNamespaceId(\"public\");\nreplacement.setAgentName(\"my-agent\");\nreplacement.setDisplayName(\"New Name\");\npersistenceService.tryUpdateAgent(replacement, current);","handlingStrategy":"validation","validationCode":"if (replacement.getVersionInfo() != null || replacement.getVersionCatalog() != null\n    || replacement.getMetaVersion() != null || replacement.getCreateTime() != null\n    || replacement.getUpdateTime() != null) {\n    throw new IllegalArgumentException(\"Agent update must not include read-only fields\");\n}","typeGuard":"boolean hasNoReadOnlyFields(Agent a) {\n    return a.getVersionInfo() == null && a.getVersionCatalog() == null\n        && a.getMetaVersion() == null && a.getCreateTime() == null && a.getUpdateTime() == null;\n}","tryCatchPattern":null,"preventionTips":["Never PUT a full Agent projection from a GET response; send only writable fields.","Use AgentUpdateRequest/AgentUpdateForm at the API boundary to constrain the payload.","Treat versionInfo, versionCatalog, metaVersion, createTime, updateTime as server-managed."],"tags":["agent","ai-registry","validation","read-only-fields","agent-update"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}