apache/dubbo · error · ClassCastException

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

Error message

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

What it means

Thrown by AbstractJsonUtilImpl.getString(Map,String) when obj contains key but its value is not a String. getString returns null for an absent key, so this ClassCastException fires only when the key is present but the value is a Number, Boolean, Map, List, or other non-String reference. It signals a schema/type mismatch: a JSON scalar/object/array where a JSON string was expected.

Source

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

            }
        }
        throw new IllegalArgumentException(String.format("value '%s' for key '%s' is not a long integer", value, key));
    }

    /**
     * Gets a string from an object for the given key.  If the key is not present, this returns null.
     * If the value is not a String, throws an exception.
     */
    @Override
    public String getString(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 String)) {
            throw new ClassCastException(
                    String.format("value '%s' for key '%s' in '%s' is not String", value, key, obj));
        }
        return (String) value;
    }

    /**
     * Casts a list of unchecked JSON values to a list of checked objects in Java type.
     * If the given list contains a value that is not a Map, throws an exception.
     */
    @SuppressWarnings("unchecked")
    @Override
    public List<Map<String, ?>> checkObjectList(List<?> rawList) {
        assert rawList != null;
        for (int i = 0; i < rawList.size(); i++) {
            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));
            }

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Inspect obj.get(key)'s runtime type and reconcile with the expected schema.
  2. If the value may legitimately be non-string, coerce with String.valueOf(raw) before use.
  3. Normalize the producer to emit strings for that field.
  4. Catch ClassCastException and report the offending key/value for triage.

Example fix

// before
String name = jsonUtil.getString(obj, "name"); // name is Integer 42
// after - coerce safely
Object raw = obj.get("name");
String name = raw == null ? null : (raw instanceof String ? (String) raw : String.valueOf(raw));
Defensive patterns

Strategy: type-guard

Validate before calling

Object raw = obj.get(key);
if (raw != null && !(raw instanceof String)) {
    return String.valueOf(raw); // coerce non-string scalars
}
return jsonUtil.getString(obj, key);

Type guard

private static boolean isStringOrNull(Object value) {
    return value == null || value instanceof String;
}

Try / catch

try {
    return jsonUtil.getString(obj, key);
} catch (ClassCastException e) {
    Object raw = obj.get(key);
    return raw == null ? null : String.valueOf(raw); // coerce instead of failing
}

Prevention

When it happens

Trigger: jsonUtil.getString(obj, key) where obj.get(key) exists and is not a String — e.g. JSON {"name": 42} or {"name": true} or {"name": {"first":"a"}} with getString(obj, "name").

Common situations: A field that was a string got changed to a number/boolean upstream; an ID field switching from numeric string to actual number; a conditional payload where the field is sometimes text and sometimes a structured object; an enum/code field returned as a number; localization turning a value into a nested object.

Related errors


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