alibaba/nacos · error · IllegalArgumentException
{} contains a non-string JSON object key
Error message
{} contains a non-string JSON object key What it means
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.
Source
Thrown at ai/src/main/java/com/alibaba/nacos/ai/service/agent/metadata/AgentResourceExtSerializer.java:201
if (value instanceof Double) {
if (!Double.isFinite((Double) value)) {
throw new IllegalArgumentException(fieldName + " must be a finite JSON number");
}
return;
}
if (value instanceof Number) {
return;
}
if (value instanceof List) {
for (Object item : (List<?>) value) {
validateJsonValue(item, fieldName);
}
return;
}
if (value instanceof Map) {
for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
if (!(entry.getKey() instanceof String)) {
throw new IllegalArgumentException(
fieldName + " contains a non-string JSON object key");
}
validateJsonValue(entry.getValue(), fieldName);
}
return;
}
throw new IllegalArgumentException(fieldName + " is not a JSON value");
}
private static void validateCatalog(AgentVersionCatalog catalog) {
AgentModelValidator.validateVersionCatalog(catalog);
List<AgentVersionCatalogEntry> versions = catalog.getOnlineVersions();
for (int i = 1; i < versions.size(); i++) {
String previous = versions.get(i - 1).getVersion();
String current = versions.get(i).getVersion();
if (AgentVersionComparator.compare(previous, current) <= 0) {
throw new IllegalArgumentException(
"Agent Version catalog must use descending Version order");View on GitHub (pinned to 9b989acdf1)
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.
Example fix
// before
Map<Object,Object> inner = new HashMap<>();
inner.put(7, "v"); // nested non-string key
ext.put("groups", inner);
// after
Map<String,Object> inner = new LinkedHashMap<>();
inner.put("7", "v");
ext.put("groups", inner); Defensive patterns
Strategy: type-guard
Validate before calling
static void assertStringKeysDeep(Object v, String path) {
if (v instanceof Map) for (Object k : ((Map<?,?>) v).keySet()) {
if (!(k instanceof String)) throw new IllegalArgumentException("non-string key at " + path);
((Map<?,?>) v).forEach((kk, vv) -> assertStringKeysDeep(vv, path + "." + kk));
} else if (v instanceof List) for (Object item : (List<?>) v) assertStringKeysDeep(item, path);
} Type guard
static boolean allKeysAreStringsDeep(Object v) {
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; }
if (v instanceof List) { for (Object i : (List<?>)v) if (!allKeysAreStringsDeep(i)) return false; }
return true;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Importing a Java Map<Integer,...> structure into extensions, or using an enum as a nested map key.
Related errors
- Agent extensions contains a non-string JSON object key
- {} is not a JSON value
- extensions exceeds 32 entries
- Unable to serialize Agent extensions
- Agent extensions exceeds {} bytes
AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14).
Data as JSON: /api/errors/290d62271140739a.
Report an issue: GitHub.