apache/dubbo · error · ClassCastException

value '%s' for idx %d in '%s' is not string

Error message

value '%s' for idx %d in '%s' is not string

What it means

Thrown by AbstractJsonUtilImpl.checkStringList(List) when iterating rawList and an element is not a String. The method casts an unchecked List<?> into List<String>, validating each entry; the first non-String entry aborts with a ClassCastException naming the value, its index, and the whole list. getListOfStrings delegates to getList then checkStringList, so the same exception surfaces through that path.

Source

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

            if (!(rawList.get(i) instanceof Map)) {
                throw new ClassCastException(
                        String.format("value %s for idx %d in %s is not object", rawList.get(i), i, rawList));
            }
        }
        return (List<Map<String, ?>>) rawList;
    }

    /**
     * Casts a list of unchecked JSON values to a list of String. If the given list
     * contains a value that is not a String, throws an exception.
     */
    @SuppressWarnings("unchecked")
    @Override
    public List<String> checkStringList(List<?> rawList) {
        assert rawList != null;
        for (int i = 0; i < rawList.size(); i++) {
            if (!(rawList.get(i) instanceof String)) {
                throw new ClassCastException(
                        String.format("value '%s' for idx %d in '%s' is not string", rawList.get(i), i, rawList));
            }
        }
        return (List<String>) rawList;
    }
}

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Normalize each entry with String.valueOf before validation, or filter to String entries: raw.stream().filter(String.class::isInstance).map(String.class::cast).collect(toList()).
  2. If the schema requires all-string entries, fix the upstream producer to emit strings.
  3. Use getList (untyped) if heterogeneous entries are valid, then coerce per element.
  4. Catch ClassCastException and report the offending index for triage.

Example fix

// before
List<String> tags = jsonUtil.checkStringList(raw); // contains Integer 7
// after - coerce all entries to String
List<String> tags = raw.stream()
    .map(java.util.Objects::toString)
    .collect(java.util.stream.Collectors.toList());
Defensive patterns

Strategy: type-guard

Validate before calling

List<?> raw = ...;
for (Object e : raw) {
    if (!(e instanceof String)) {
        raw = raw.stream().map(java.util.Objects::toString).collect(java.util.stream.Collectors.toList());
        break;
    }
}
return jsonUtil.checkStringList(raw);

Type guard

private static boolean allStrings(List<?> rawList) {
    for (Object e : rawList) if (!(e instanceof String)) return false;
    return true;
}

Try / catch

try {
    return jsonUtil.checkStringList(rawList);
} catch (ClassCastException e) {
    // message names the offending index; coerce all entries to String
    return rawList.stream()
        .map(java.util.Objects::toString)
        .collect(java.util.stream.Collectors.toList());
}

Prevention

When it happens

Trigger: jsonUtil.checkStringList(rawList) (or getListOfStrings(obj,key)) where rawList contains at least one element that is not a String — e.g. [1, 2, 3], [{"a":1}], [true]. A list of pure strings passes.

Common situations: A JSON array of numbers/booleans where an array of strings was expected; a backend that emits numeric IDs as JSON numbers instead of strings; mixed-type arrays from a lenient producer; an enum/code list returned as numbers; a payload version change.

Related errors


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