grpc/grpc-java · 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

JsonUtil.getList looks up a key in a parsed JSON object (Map<String,?>) and expects the value to be a JSON array (List). If the key exists but the value is any other JSON type, it throws ClassCastException with a message showing the value, key, and containing object.

Source

Thrown at core/src/main/java/io/grpc/internal/JsonUtil.java:45

/**
 * Helper utility to work with JSON values in Java types. Includes the JSON dialect used by
 * Protocol Buffers.
 */
public class JsonUtil {
  /**
   * Gets a list from an object for the given key.  If the key is not present, this returns null.
   * If the value is not a List, throws an exception.
   */
  @Nullable
  public static List<?> getList(Map<String, ?> obj, String key) {
    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.
   */
  @Nullable
  public static List<Map<String, ?>> getListOfObjects(Map<String, ?> obj, String key) {
    List<?> list = getList(obj, key);
    if (list == null) {
      return null;
    }
    return checkObjectList(list);
  }

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Fix the JSON so the key holds an array: change "key": "value" to "key": ["value"]
  2. Validate the config against the expected schema before feeding it to gRPC
  3. Check which file produced the object (it is printed in the message) and correct that source
  4. If a single item is allowed, check the producer's schema — newer versions may accept either shape

Example fix

// before
{"methodConfig": {"name": [{}]} }
// after
{"methodConfig": [ {"name": [{}]} ] }
Defensive patterns

Strategy: validation

Validate before calling

Object v = cfg.get("methodConfig");
if (v != null && !(v instanceof List)) throw new IllegalArgumentException("methodConfig must be an array");

Type guard

List<?> asList(Object v) { return v instanceof List ? (List<?>) v : null; }

Try / catch

try { JsonUtil.getList(obj, "methodConfig"); }
catch (ClassCastException e) { log.error("bad service config: {}", e.getMessage()); throw new InvalidConfigException(e); }

Prevention

When it happens

Trigger: Calling getList/list on a config object (e.g. gRPC service config, xDS bootstrap JSON) where the target key holds a string, number, boolean, or object instead of an array.

Common situations: Hand-edited service config or xDS bootstrap files where a field like 'methodConfig' or a retry list was written as a single object/string; config generated by tooling with a schema change; typo reusing a key that stores a scalar.

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


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/3b526e029041e9e2. Report an issue: GitHub.