grpc/grpc-java · error · ClassCastException
value ' ' for key ' ' in ' ' is not object
Error message
value '%s' for key '%s' in '%s' is not object
What it means
JsonUtil.getObject retrieves a key from a parsed JSON object and requires the value to be a JSON object (Map). If the key exists with any other type, it throws ClassCastException('value ... is not object'), embedding the offending value, key, and parent object in the message.
Solutions
- Fix the JSON so the key maps to an object: "key": {...} not "key": "..."
- Validate bootstrap/service-config JSON against the gRPC schema before startup
- Open the file named indirectly by the message and re-check the nesting of that key
- Regenerate the config with the tooling's current version to eliminate stale shapes
Example fix
// before
{"xds_server": "trafficdirector.googleapis.com"}
// after
{"xds_server": {"server_uri": "trafficdirector.googleapis.com", "channel_creds": [...]}} Defensive patterns
Strategy: validation
Validate before calling
Object v = cfg.get("node");
if (v != null && !(v instanceof Map)) throw new IllegalArgumentException("node must be a JSON object"); Type guard
@SuppressWarnings("unchecked")
Map<String,?> asObject(Object v) { return v instanceof Map ? (Map<String,?>) v : null; } Try / catch
try { JsonUtil.getObject(bootstrap, "node"); }
catch (ClassCastException e) { log.error("bad xDS bootstrap: {}", e.getMessage()); throw new InvalidConfigException(e); } Prevention
- Schema-validate xDS bootstrap and service-config files before startup
- Watch for field-shape changes when upgrading config generators
- Keep nesting correct: objects for sections, arrays for lists
When it happens
Trigger: Calling getObject/getList-of-objects on a JSON config where the key holds a scalar or array instead of a nested object — e.g. xDS bootstrap 'node' or 'xds_server' written as a string, or a nested section accidentally flattened.
Common situations: Hand-written gRPC service config / xDS bootstrap files with wrong nesting; version drift where a field changed from object to scalar between config generators; copy-paste errors merging JSON sections.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- value ' ' for key ' ' in ' ' is not a number
- value ' ' for key ' ' in ' ' is not List
- Authorization policy should be a JSON object. Found: null
- Number expected to be integer:
- Number expected to be long:
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/9bee73af894a391e.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/io/grpc/internal/JsonUtil.java:92
return null;
}
return checkStringList(list);
}
/**
* Gets an object from an object for the given key. If the key is not present, this returns null.
* If the value is not a Map, throws an exception.
*/
@SuppressWarnings("unchecked")
@Nullable
public static Map<String, ?> getObject(Map<String, ?> obj, String key) {
assert key != null;
if (!obj.containsKey(key)) {
return null;
}
Object value = obj.get(key);
if (!(value instanceof Map)) {
throw new ClassCastException(
String.format("value '%s' for key '%s' in '%s' is not object", value, key, obj));
}
return (Map<String, ?>) value;
}
/**
* Gets a number from an object for the given key. If the key is not present, this returns null.
* If the value does not represent a double, throws an exception.
*/
@Nullable
public static Double getNumberAsDouble(Map<String, ?> obj, String key) {
assert key != null;
if (!obj.containsKey(key)) {
return null;
}
Object value = obj.get(key);
if (value instanceof Double) {
return (Double) value;View on GitHub (pinned to 64daddc1f3)