grpc/grpc-java · error · ClassCastException

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

Error message

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

What it means

getString requires that a key present in the JSON object maps to a String; otherwise it throws ClassCastException naming the value, key, and whole object. gRPC uses it when parsing string config fields (e.g. LB policy names, target strings), and strict typing avoids silent coercion bugs.

Solutions

  1. Quote the value in the JSON so it is a string: 5 -> "5".
  2. Check the third '%s' in the message to see the whole offending object and locate the mis-typed field.
  3. Add schema validation for string fields before passing config to gRPC.

Example fix

// before
{"loadBalancingPolicy": 5}
// after
{"loadBalancingPolicy": "round_robin"}
Defensive patterns

Strategy: type-guard

Validate before calling

Object v = config.get("loadBalancingPolicy");
if (v != null && !(v instanceof String)) {
  throw new IllegalArgumentException("loadBalancingPolicy must be a string, got: " + v.getClass());
}

Type guard

boolean isString(Object v) { return v instanceof String; }

Try / catch

try {
  String policy = JsonUtil.getString(config, "loadBalancingPolicy");
} catch (ClassCastException e) {
  log.error("String config field has wrong type: " + e.getMessage());
}

Prevention

When it happens

Trigger: JsonUtil.getString(map, key) where the value is a number, boolean, list, or object — e.g. {"loadBalancingPolicy": 5} or {"method": {"a":1}}.

Common situations: Service config with an unquoted number or boolean where a string is expected; JSON tooling that auto-types values (dates, IDs becoming numbers); nested object pasted into a string field.

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/b9a5b076435849d8. Report an issue: GitHub.

Appendix: source

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

      }
    }
    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.
   */
  @Nullable
  public static String getString(Map<String, ?> obj, String key) {
    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;
  }

  /**
   * Gets a string from an object for the given key, parsed as a duration (defined by protobuf).  If
   * the key is not present, this returns null.  If the value is not a String or not properly
   * formatted, throws an exception.
   */
  public static Long getStringAsDuration(Map<String, ?> obj, String key) {
    String value = getString(obj, key);
    if (value == null) {
      return null;
    }
    try {
      return parseDuration(value);
    } catch (ParseException e) {

View on GitHub (pinned to 64daddc1f3)