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

  1. Use only String keys at every nesting level inside extension values.
  2. Coerce keys with String.valueOf before insertion.
  3. 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

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


AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14). Data as JSON: /api/errors/290d62271140739a. Report an issue: GitHub.