{"record":{"id":"290d62271140739a","repo":"alibaba/nacos","slug":"contains-a-non-string-json-object-key","errorCode":null,"errorMessage":"{} contains a non-string JSON object key","messagePattern":"(.+?) contains a non-string JSON object key","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/com/alibaba/nacos/ai/service/agent/metadata/AgentResourceExtSerializer.java","lineNumber":201,"sourceCode":"        if (value instanceof Double) {\n            if (!Double.isFinite((Double) value)) {\n                throw new IllegalArgumentException(fieldName + \" must be a finite JSON number\");\n            }\n            return;\n        }\n        if (value instanceof Number) {\n            return;\n        }\n        if (value instanceof List) {\n            for (Object item : (List<?>) value) {\n                validateJsonValue(item, fieldName);\n            }\n            return;\n        }\n        if (value instanceof Map) {\n            for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {\n                if (!(entry.getKey() instanceof String)) {\n                    throw new IllegalArgumentException(\n                        fieldName + \" contains a non-string JSON object key\");\n                }\n                validateJsonValue(entry.getValue(), fieldName);\n            }\n            return;\n        }\n        throw new IllegalArgumentException(fieldName + \" is not a JSON value\");\n    }\n    \n    private static void validateCatalog(AgentVersionCatalog catalog) {\n        AgentModelValidator.validateVersionCatalog(catalog);\n        List<AgentVersionCatalogEntry> versions = catalog.getOnlineVersions();\n        for (int i = 1; i < versions.size(); i++) {\n            String previous = versions.get(i - 1).getVersion();\n            String current = versions.get(i).getVersion();\n            if (AgentVersionComparator.compare(previous, current) <= 0) {\n                throw new IllegalArgumentException(\n                    \"Agent Version catalog must use descending Version order\");","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/ai/src/main/java/com/alibaba/nacos/ai/service/agent/metadata/AgentResourceExtSerializer.java#L183-L219","documentation":"validateJsonValue() recurses into Map values; when it finds a Map whose key is not a String it rejects with '<fieldName> contains a non-string JSON object key'. Like the top-level check, this only happens for programmatically built nested maps, since JSON object keys are always strings on the wire.","triggerScenarios":"An extensions value is a nested Map built with a non-String key (Integer, enum, etc.), e.g. a grouping object keyed by numeric ids.","commonSituations":"Importing a Java Map<Integer,...> structure into extensions, or using an enum as a nested map key.","solutions":["Use only String keys at every nesting level inside extension values.","Coerce keys with String.valueOf before insertion.","Validate the structure with a recursive key-type check in tests."],"exampleFix":"// before\nMap<Object,Object> inner = new HashMap<>();\ninner.put(7, \"v\"); // nested non-string key\next.put(\"groups\", inner);\n\n// after\nMap<String,Object> inner = new LinkedHashMap<>();\ninner.put(\"7\", \"v\");\next.put(\"groups\", inner);","handlingStrategy":"type-guard","validationCode":"static void assertStringKeysDeep(Object v, String path) {\n    if (v instanceof Map) for (Object k : ((Map<?,?>) v).keySet()) {\n        if (!(k instanceof String)) throw new IllegalArgumentException(\"non-string key at \" + path);\n        ((Map<?,?>) v).forEach((kk, vv) -> assertStringKeysDeep(vv, path + \".\" + kk));\n    } else if (v instanceof List) for (Object item : (List<?>) v) assertStringKeysDeep(item, path);\n}","typeGuard":"static boolean allKeysAreStringsDeep(Object v) {\n    if (v instanceof Map) { for (Object k : ((Map<?,?>)v).keySet()) { if (!(k instanceof String)) return false; } for (Object vv : ((Map<?,?>)v).values()) if (!allKeysAreStringsDeep(vv)) return false; }\n    if (v instanceof List) { for (Object i : (List<?>)v) if (!allKeysAreStringsDeep(i)) return false; }\n    return true;\n}","tryCatchPattern":null,"preventionTips":["Build nested extension values with Map<String,Object> only.","Coerce non-string keys with String.valueOf at insertion.","Add a recursive key-type check in tests."],"tags":["ai-registry","agent","extensions","type-violation"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}