apache/dubbo · error · ClassCastException

value '%s' for key '%s' in '%s' is not List

Error message

value '%s' for key '%s' in '%s' is not List

What it means

Thrown by AbstractJsonUtilImpl.getList(Map,String) when obj contains key but its value is not a java.util.List. getList returns null for an absent key, so this exception only fires when the key IS present but the JSON value is a scalar, object, or other non-list type. It is a ClassCastException reflecting a schema/type mismatch between the JSON shape and the expected Java type. Subclasses (the JsonUtil impls) inherit this behavior.

Source

Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/json/impl/AbstractJsonUtilImpl.java:55

            List<String> list = new LinkedList<>();
            list.add("json");
            return CollectionUtils.equals(list, toJavaList(toJson(list), String.class));
        } catch (Throwable t) {
            return false;
        }
    }

    @Override
    public List<?> getList(Map<String, ?> obj, String key) {
        assert obj != null;
        assert key != null;
        if (!obj.containsKey(key)) {
            return null;
        }
        Object value = obj.get(key);
        if (!(value instanceof List)) {
            throw new ClassCastException(String.format("value '%s' for key '%s' in '%s' is not List", value, key, obj));
        }
        return (List<?>) value;
    }

    /**
     * Gets a list from an object for the given key, and verifies all entries are objects.  If the key
     * is not present, this returns null.  If the value is not a List or an entry is not an object,
     * throws an exception.
     */
    @Override
    public List<Map<String, ?>> getListOfObjects(Map<String, ?> obj, String key) {
        assert obj != null;
        List<?> list = getList(obj, key);
        if (list == null) {
            return null;
        }
        return checkObjectList(list);
    }

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Confirm the actual runtime type of obj.get(key) and reconcile with the schema — typically the field shape changed upstream.
  2. If the field may legitimately be a scalar or a list, branch on instanceof List before calling getList.
  3. Switch to a more permissive accessor or normalize the JSON to the expected array shape upstream.
  4. Catch ClassCastException and report the offending key/value for triage.

Example fix

// before
List<?> items = jsonUtil.getList(obj, "items");
// after - tolerate single-or-list
Object raw = obj.get("items");
List<?> items = raw instanceof List ? (List<?>) raw : (raw == null ? null : List.of(raw));
Defensive patterns

Strategy: type-guard

Validate before calling

Object raw = obj.get(key);
if (raw != null && !(raw instanceof List)) {
    // field present but not a list; decide policy before calling getList
    return Collections.emptyList();
}
return jsonUtil.getList(obj, key);

Type guard

private static boolean isListOfMapsOrEmpty(Object value) {
    return value == null || value instanceof List;
}

Try / catch

try {
    return jsonUtil.getList(obj, key);
} catch (ClassCastException e) {
    log.warn("expected list at key={}, got value={}", key, obj.get(key));
    return null; // or a domain-specific fallback
}

Prevention

When it happens

Trigger: jsonUtil.getList(obj, key) where obj.get(key) exists and is a Map, String, Number, Boolean, or null-reference that is not a List instance. For example JSON {"items": {"a":1}} with getList(obj, "items").

Common situations: API contract changed so a field that was an array became a single object (or vice versa); a conditional payload where the field is sometimes a list and sometimes a scalar; downstream service returning an error object instead of the expected array; using getList on a field the schema documents as an object.

Related errors


AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14). Data as JSON: /api/errors/7d76bf6f40a16428. Report an issue: GitHub.