alibaba/nacos · error · IllegalArgumentException

AgentResourceExt must be a JSON object

Error message

AgentResourceExt must be a JSON object

What it means

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.

Source

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

    private static void putIfPresent(Map<String, Object> target, String field, Object value) {
        if (value != null) {
            target.put(field, value);
        }
    }
    
    private static void validateJsonShape(String json) {
        if (json == null || json.isEmpty()) {
            throw new IllegalArgumentException("AgentResourceExt JSON must not be empty");
        }
        validateSingleJsonValue(json);
        final Map<?, ?> root;
        try {
            root = JacksonUtils.toObj(json, Map.class);
        } catch (NacosDeserializationException e) {
            throw new IllegalArgumentException("Invalid AgentResourceExt", e);
        }
        if (root == null) {
            throw new IllegalArgumentException("AgentResourceExt must be a JSON object");
        }
        rejectUnknownFields(root, ROOT_FIELDS, "AgentResourceExt");
        validateJsonInteger(root, "schemaVersion");
        validateOptionalJsonText(root, "displayName");
        validateOptionalJsonText(root, "iconUrl");
        validateProviderShape(root);
        validateExtensionsShape(root);
        validateCatalogShape(root);
    }
    
    private static void validateProviderShape(Map<?, ?> root) {
        if (!root.containsKey("provider")) {
            return;
        }
        Map<?, ?> provider = requireJsonObject(root.get("provider"), "provider");
        rejectUnknownFields(provider, PROVIDER_FIELDS, "AgentProvider");
        validateRequiredJsonText(provider, "name");
        validateOptionalJsonText(provider, "url");

View on GitHub (pinned to 9b989acdf1)

Solutions

  1. Ensure the ext JSON is an object, not the literal null; re-publish the agent with a proper object payload.
  2. At the write site, never persist String.valueOf(null) or a serializer that emits 'null'; skip the column or write {}-equivalent.
  3. Guard deserialize callers to treat literal "null" as 'no extension'.

Example fix

// before: stored ext is the literal string "null"

// after
AgentResourceExt ext = new AgentResourceExt();
ext.setSchemaVersion(AgentResourceExt.SCHEMA_VERSION);
ext.setVersionCatalog(catalog);
row.setExt(AgentResourceExtSerializer.serialize(ext));
Defensive patterns

Strategy: validation

Validate before calling

static AgentResourceExt safeDeserialize(String raw) {
    if (raw == null || raw.isEmpty() || "null".equals(raw.trim())) {
        return null; // treat JSON null / empty as 'no extension'
    }
    return AgentResourceExtSerializer.deserialize(raw);
}

Type guard

static boolean isJsonObjectLiteral(String raw) {
    String t = raw == null ? "" : raw.trim();
    return !t.isEmpty() && !"null".equals(t) && t.startsWith("{");
}

Prevention

When it happens

Trigger: 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.

Common situations: 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".

Related errors


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