{"record":{"id":"8e321ea116613a6c","repo":"alibaba/nacos","slug":"agentresourceext-must-be-a-json-object","errorCode":null,"errorMessage":"AgentResourceExt must be a JSON object","messagePattern":"AgentResourceExt must be a JSON object","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/com/alibaba/nacos/ai/service/agent/metadata/AgentResourceExtSerializer.java","lineNumber":306,"sourceCode":"    private static void putIfPresent(Map<String, Object> target, String field, Object value) {\n        if (value != null) {\n            target.put(field, value);\n        }\n    }\n    \n    private static void validateJsonShape(String json) {\n        if (json == null || json.isEmpty()) {\n            throw new IllegalArgumentException(\"AgentResourceExt JSON must not be empty\");\n        }\n        validateSingleJsonValue(json);\n        final Map<?, ?> root;\n        try {\n            root = JacksonUtils.toObj(json, Map.class);\n        } catch (NacosDeserializationException e) {\n            throw new IllegalArgumentException(\"Invalid AgentResourceExt\", e);\n        }\n        if (root == null) {\n            throw new IllegalArgumentException(\"AgentResourceExt must be a JSON object\");\n        }\n        rejectUnknownFields(root, ROOT_FIELDS, \"AgentResourceExt\");\n        validateJsonInteger(root, \"schemaVersion\");\n        validateOptionalJsonText(root, \"displayName\");\n        validateOptionalJsonText(root, \"iconUrl\");\n        validateProviderShape(root);\n        validateExtensionsShape(root);\n        validateCatalogShape(root);\n    }\n    \n    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\");","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/ai/src/main/java/com/alibaba/nacos/ai/service/agent/metadata/AgentResourceExtSerializer.java#L288-L324","documentation":"validateJsonShape() parses the raw JSON to a Map; if the result is null (which only happens when the JSON literal is the token 'null'), it throws 'AgentResourceExt must be a JSON object'. The ext must be a JSON object, and a JSON null is explicitly not allowed even though it is valid JSON.","triggerScenarios":"deserialize(\"null\") or a stored ai_resource.ext whose value is the literal string \"null\". The empty case is caught earlier; this is specifically the JSON null token.","commonSituations":"A previous write stored the Java string 'null' (e.g. String.valueOf(nullObject) or a serializer that emitted null), or a test/fixture using \"null\".","solutions":["Ensure the ext JSON is an object, not the literal null; re-publish the agent with a proper object payload.","At the write site, never persist String.valueOf(null) or a serializer that emits 'null'; skip the column or write {}-equivalent.","Guard deserialize callers to treat literal \"null\" as 'no extension'."],"exampleFix":"// before: stored ext is the literal string \"null\"\n\n// after\nAgentResourceExt ext = new AgentResourceExt();\next.setSchemaVersion(AgentResourceExt.SCHEMA_VERSION);\next.setVersionCatalog(catalog);\nrow.setExt(AgentResourceExtSerializer.serialize(ext));","handlingStrategy":"validation","validationCode":"static AgentResourceExt safeDeserialize(String raw) {\n    if (raw == null || raw.isEmpty() || \"null\".equals(raw.trim())) {\n        return null; // treat JSON null / empty as 'no extension'\n    }\n    return AgentResourceExtSerializer.deserialize(raw);\n}","typeGuard":"static boolean isJsonObjectLiteral(String raw) {\n    String t = raw == null ? \"\" : raw.trim();\n    return !t.isEmpty() && !\"null\".equals(t) && t.startsWith(\"{\");\n}","tryCatchPattern":null,"preventionTips":["Never persist the literal 'null' string into ai_resource.ext.","At write sites, skip the column or write a real object when there is no extension.","Treat literal 'null' as 'no extension' at read sites."],"tags":["ai-registry","agent","deserialization","type-violation"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}