alibaba/nacos · error · IllegalArgumentException

{} must be an array

Error message

{} must be an array

What it means

Inside each versionCatalog.onlineVersions entry, the 'labels' and 'protocols' fields must each be a JSON array. The validateStringArray helper (line 356) throws this when either field is present but is not an array (e.g. a string or number). The field name is interpolated into the message so you know which one failed.

Source

Thrown at ai/src/main/java/com/alibaba/nacos/ai/service/agent/metadata/AgentResourceExtSerializer.java:357

        validateOptionalJsonText(catalog, "latestVersion");
        Object versionsValue = catalog.get("onlineVersions");
        if (!(versionsValue instanceof List)) {
            throw new IllegalArgumentException(
                "AgentVersionCatalog onlineVersions must be an array");
        }
        for (Object entryValue : (List<?>) versionsValue) {
            Map<?, ?> entry = requireJsonObject(entryValue, "versionCatalog entry");
            rejectUnknownFields(entry, CATALOG_ENTRY_FIELDS, "AgentVersionCatalogEntry");
            validateRequiredJsonText(entry, "version");
            validateStringArray(entry, "labels");
            validateStringArray(entry, "protocols");
        }
    }
    
    private static void validateStringArray(Map<?, ?> object, String field) {
        Object value = object.get(field);
        if (!(value instanceof List)) {
            throw new IllegalArgumentException(field + " must be an array");
        }
        for (Object item : (List<?>) value) {
            if (!(item instanceof String)) {
                throw new IllegalArgumentException(field + " must contain only strings");
            }
        }
    }
    
    private static Map<?, ?> requireJsonObject(Object value, String fieldName) {
        if (!(value instanceof Map)) {
            throw new IllegalArgumentException(fieldName + " must be a JSON object");
        }
        return (Map<?, ?>) value;
    }
    
    private static void validateRequiredJsonText(Map<?, ?> object, String field) {
        if (!(object.get(field) instanceof String)) {
            throw new IllegalArgumentException(field + " must be a string");

View on GitHub (pinned to 9b989acdf1)

Solutions

  1. Wrap the value in square brackets: change "protocols":"a2a" to "protocols":["a2a"].
  2. If the field should be absent, omit it entirely rather than using a non-array placeholder.
  3. Ensure your DTO serializes List<String> fields as JSON arrays.

Example fix

// before
{"version":"1.0.0","labels":"stable","protocols":"a2a"}
// after
{"version":"1.0.0","labels":["stable"],"protocols":["a2a"]}
Defensive patterns

Strategy: validation

Validate before calling

private static void ensureStringArray(Map<?,?> entry, String field) {
    Object value = entry.get(field);
    if (value != null && !(value instanceof List)) {
        throw new IllegalArgumentException(field + " must be an array");
    }
}

Type guard

public static boolean isStringArrayField(Map<?,?> obj, String field) {
    Object value = obj == null ? null : obj.get(field);
    return value instanceof List;
}

Try / catch

try {
    AgentResourceExtSerializer.deserialize(json);
} catch (IllegalArgumentException e) {
    if (e.getMessage().endsWith("must be an array")) {
        // identify the field from the message and fix the JSON
    }
    throw e;
}

Prevention

When it happens

Trigger: Deserializing ai_resource.ext where a catalog entry's 'labels' or 'protocols' is a non-array JSON value. For example, "protocols":"a2a" instead of "protocols":["a2a"].

Common situations: Shorthand notation where a single protocol or label is written as a bare string; schema migration from a format that used comma-separated strings; incorrect client-side DTO mapping.

Related errors


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