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
- 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()).
- If the schema requires all-string entries, fix the upstream producer to emit strings.
- Use getList (untyped) if heterogeneous entries are valid, then coerce per element.
- 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
- Coerce list entries with String.valueOf/Objects::toString before validation when non-strings are possible.
- Validate array element types against the schema at trust boundaries.
- Use getList (untyped) with per-element handling if mixed types are legitimate.
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
- value %s for idx %d in %s is not object
- value '%s' for key '%s' in '%s' is not List
- value '%s' for key '%s' in '%s' is not object
- value '%s' for key '%s' in '%s' is not String
- value '%s' for key '%s' in '%s' is not a number
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/2be63545036faeca.
Report an issue: GitHub.