{"record":{"id":"37ae995b1bc47ea3","repo":"alibaba/nacos","slug":"missing-agentresourceext-field-versioncatalog","errorCode":null,"errorMessage":"Missing AgentResourceExt field: versionCatalog","messagePattern":"Missing AgentResourceExt field: versionCatalog","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/com/alibaba/nacos/ai/service/agent/metadata/AgentResourceExtSerializer.java","lineNumber":335,"sourceCode":"    private static void validateProviderShape(Map<?, ?> root) {\n        if (!root.containsKey(\"provider\")) {\n            return;\n        }\n        Map<?, ?> provider = requireJsonObject(root.get(\"provider\"), \"provider\");\n        rejectUnknownFields(provider, PROVIDER_FIELDS, \"AgentProvider\");\n        validateRequiredJsonText(provider, \"name\");\n        validateOptionalJsonText(provider, \"url\");\n    }\n    \n    private static void validateExtensionsShape(Map<?, ?> root) {\n        if (root.containsKey(\"extensions\")) {\n            requireJsonObject(root.get(\"extensions\"), \"extensions\");\n        }\n    }\n    \n    private static void validateCatalogShape(Map<?, ?> root) {\n        if (!root.containsKey(\"versionCatalog\")) {\n            throw new IllegalArgumentException(\"Missing AgentResourceExt field: versionCatalog\");\n        }\n        Map<?, ?> catalog = requireJsonObject(root.get(\"versionCatalog\"), \"versionCatalog\");\n        rejectUnknownFields(catalog, CATALOG_FIELDS, \"AgentVersionCatalog\");\n        validateOptionalJsonText(catalog, \"latestVersion\");\n        Object versionsValue = catalog.get(\"onlineVersions\");\n        if (!(versionsValue instanceof List)) {\n            throw new IllegalArgumentException(\n                \"AgentVersionCatalog onlineVersions must be an array\");\n        }\n        for (Object entryValue : (List<?>) versionsValue) {\n            Map<?, ?> entry = requireJsonObject(entryValue, \"versionCatalog entry\");\n            rejectUnknownFields(entry, CATALOG_ENTRY_FIELDS, \"AgentVersionCatalogEntry\");\n            validateRequiredJsonText(entry, \"version\");\n            validateStringArray(entry, \"labels\");\n            validateStringArray(entry, \"protocols\");\n        }\n    }\n    ","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/ai/src/main/java/com/alibaba/nacos/ai/service/agent/metadata/AgentResourceExtSerializer.java#L317-L353","documentation":"The JSON stored in ai_resource.ext must contain a top-level 'versionCatalog' object. This validator (AgentResourceExtSerializer.validateCatalogShape) rejects any root JSON that omits it, because the version catalog is the mandatory carrier of an Agent's online Version list and protocol bindings. Without it the Agent resource is incomplete and cannot be serialized or deserialized.","triggerScenarios":"Calling AgentResourceExtSerializer.deserialize(json) or serialize(obj) on JSON/object where the root object has no 'versionCatalog' key. Also triggered when persisting an AgentResourceExt whose getVersionCatalog() returns null (via the typed validate path → AgentModelValidator).","commonSituations":"Hand-editing the ai_resource.ext JSON column and forgetting the versionCatalog block; migrating from an older schema that did not require versionCatalog; building AgentResourceExt programmatically without setting the catalog; importing agent metadata from another system that uses a different shape.","solutions":["Add a 'versionCatalog' object to the root JSON with at least an 'onlineVersions' array.","If building programmatically, call resourceExt.setVersionCatalog(catalog) with a non-null AgentVersionCatalog that has at least one online version entry.","Validate with AgentResourceExtSerializer.validate(obj) before persisting to catch this earlier in the pipeline."],"exampleFix":"// before\n{\"schemaVersion\":1,\"displayName\":\"my-agent\"}\n// after\n{\"schemaVersion\":1,\"displayName\":\"my-agent\",\"versionCatalog\":{\"latestVersion\":\"1.0.0\",\"onlineVersions\":[{\"version\":\"1.0.0\",\"labels\":[],\"protocols\":[\"a2a\"]}]}}","handlingStrategy":"validation","validationCode":"if (resourceExt.getVersionCatalog() == null) {\n    throw new IllegalStateException(\"versionCatalog is required before serialization\");\n}\nAgentResourceExtSerializer.validate(resourceExt);","typeGuard":"public static boolean hasValidCatalog(AgentResourceExt ext) {\n    return ext != null && ext.getVersionCatalog() != null\n        && ext.getVersionCatalog().getOnlineVersions() != null;\n}","tryCatchPattern":"try {\n    AgentResourceExtSerializer.deserialize(json);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"versionCatalog\")) {\n        // log and provide a default empty catalog or reject the input\n    }\n    throw e;\n}","preventionTips":["Always set versionCatalog on AgentResourceExt before calling serialize().","Run AgentResourceExtSerializer.validate(obj) in unit tests for every Agent resource you build.","Use the toStorageProjection output format as your JSON template reference."],"tags":["validation","ai-agent","json-schema","serialization"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}