grpc/grpc-java · error · IllegalArgumentException

The value of the map entry

Error message

The value of the map entry '${entry}' is of type '${value.getClass()}', which is not supported

What it means

When building the channel from option maps (e.g. raw service-config or custom option maps), ManagedChannelImplBuilder normalizes each map entry value to String, Double, or Boolean. A value of any other type (e.g. nested Map, List, null wrapper) cannot be represented and triggers this IllegalArgumentException naming the offending key and its class.

Solutions

  1. Flatten nested values to supported types: String, Number, or Boolean before setting the option.
  2. Serialize nested structures to a JSON string if the option accepts textual config.
  3. Log/inspect the offending key ('entry') and fix its value type.
  4. Use typed builder methods (e.g. .maxInboundMessageSize(int)) instead of raw maps.

Example fix

// before
Map<String, Object> opts = new HashMap<>();
opts.put("serviceConfig", someNestedMap); // Map value
easyBuilder.build();
// after
opts.put("serviceConfig", jsonAsString); // String value
Defensive patterns

Strategy: validation

Validate before calling

static void checkOptionTypes(Map<String, Object> opts) {
  for (Map.Entry<String, Object> e : opts.entrySet()) {
    Object v = e.getValue();
    if (!(v instanceof String || v instanceof Number || v instanceof Boolean)) {
      throw new IllegalArgumentException("option '" + e.getKey() + "' has unsupported type " + v.getClass());
    }
  }
}

Type guard

boolean isSupportedOptionValue(Object v) {
  return v instanceof String || v instanceof Number || v instanceof Boolean;
}

Try / catch

try {
  buildChannel(options);
} catch (IllegalArgumentException e) {
  if (e.getMessage() != null && e.getMessage().contains("which is not supported")) {
    log.error("Bad option map: {}", e.getMessage());
  } else throw e;
}

Prevention

When it happens

Trigger: Passing a Map<String,?> channel option or config map containing a nested Map, List, or custom object value for a key, then invoking the internal builder's map-parsing path.

Common situations: Constructing service config options programmatically with nested structures; JSON configs deserialized into java.util.LinkedHashMap values leaking into option maps; client interceptor option maps carrying POJOs.

Related errors


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

Appendix: source

Thrown at core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java:632

          entry.getKey() instanceof String,
          "The key of the entry '%s' is not of String type", entry);

      String key = (String) entry.getKey();
      Object value = entry.getValue();
      if (value == null) {
        parsedMap.put(key, null);
      } else if (value instanceof Map) {
        parsedMap.put(key, checkMapEntryTypes((Map<?, ?>) value));
      } else if (value instanceof List) {
        parsedMap.put(key, checkListEntryTypes((List<?>) value));
      } else if (value instanceof String) {
        parsedMap.put(key, value);
      } else if (value instanceof Number) {
        parsedMap.put(key, ((Number) value).doubleValue());
      } else if (value instanceof Boolean) {
        parsedMap.put(key, value);
      } else {
        throw new IllegalArgumentException(
            "The value of the map entry '" + entry + "' is of type '" + value.getClass()
                + "', which is not supported");
      }
    }
    return Collections.unmodifiableMap(parsedMap);
  }

  private static List<?> checkListEntryTypes(List<?> list) {
    List<Object> parsedList = new ArrayList<>(list.size());
    for (Object value : list) {
      if (value == null) {
        parsedList.add(null);
      } else if (value instanceof Map) {
        parsedList.add(checkMapEntryTypes((Map<?, ?>) value));
      } else if (value instanceof List) {
        parsedList.add(checkListEntryTypes((List<?>) value));
      } else if (value instanceof String) {
        parsedList.add(value);

View on GitHub (pinned to 64daddc1f3)