grpc/grpc-java · error · RuntimeException
There are fields in a LoadBalancingConfig object. Exactly…
Error message
There are ${size} fields in a LoadBalancingConfig object. Exactly one is expected. Config=${lbConfig} What it means
gRPC service configs express the load balancing policy selection as a JSON object with exactly one entry: the policy name as key and its config object as value (e.g. {"round_robin":{}}). ServiceConfigUtil.unwrapLoadBalancingConfig enforces this one-entry shape; any other number of fields makes the selected policy ambiguous, so it throws a RuntimeException.
Solutions
- Ensure the object has exactly one key: the policy name mapped to its config object
- Split multiple policies into a list of single-entry objects: [{"grpclb":{}},{"round_robin":{}}] (list order = preference order)
- If no config is needed, still provide an empty object for the policy, e.g. {"round_robin":{}}
Example fix
// before
"loadBalancingConfig": {"round_robin": {}, "grpclb": {}}
// after
"loadBalancingConfig": [{"grpclb": {}}, {"round_robin": {}}] Defensive patterns
Strategy: validation
Validate before calling
// Pre-validate loadBalancingConfig shape
if (lbConfig != null) {
if (lbConfig.size() != 1) {
throw new IllegalArgumentException(
"loadBalancingConfig object must have exactly one entry, got " + lbConfig.size());
}
} Type guard
boolean isSingleEntryLbConfig(java.util.Map<String, ?> cfg) {
return cfg != null && cfg.size() == 1;
} Try / catch
try {
LbConfig cfg = ServiceConfigUtil.unwrapLoadBalancingConfig(obj);
} catch (RuntimeException e) {
log.error("Malformed loadBalancingConfig (need exactly one policy per object): " + e.getMessage());
throw new ConfigInvalidException(e);
} Prevention
- Express policy preference as a LIST of single-entry objects, not one multi-entry object
- Keep policy name as the sole key with an empty object if no params: {"round_robin":{}}
- Schema-validate service configs before rollout
When it happens
Trigger: Passing a loadBalancingConfig object with zero entries or multiple entries (e.g. {"round_robin":{},"grpclb":{}}) into unwrapLoadBalancingConfig, typically via unwrapLoadBalancingConfigList while parsing the service config.
Common situations: Hand-merged service configs where two policies were combined into one object; empty config objects; misunderstanding that each list element must contain exactly one policy, with selection priority expressed by the list order.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Can not convert status code
- Cluster " + cluster.getName() + ": invalid ring hash…
- Cluster " + cluster.getName() + ": unsupported lb policy: "…
- Failed to parse lb config for cluster '" +…
- Invalid duration in weighted round robin config: " +…
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/ad4e38dc8d5dde2c.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/io/grpc/internal/ServiceConfigUtil.java:313
String policy = JsonUtil.getString(serviceConfig, "loadBalancingPolicy");
if (policy != null) {
// Convert the policy to a config, so that the caller can handle them in the same way.
policy = policy.toLowerCase(Locale.ROOT);
Map<String, ?> fakeConfig = Collections.singletonMap(policy, Collections.emptyMap());
lbConfigs.add(fakeConfig);
}
}
return Collections.unmodifiableList(lbConfigs);
}
/**
* Unwrap a LoadBalancingConfig JSON object into a {@link LbConfig}. The input is a JSON object
* (map) with exactly one entry, where the key is the policy name and the value is a config object
* for that policy.
*/
public static LbConfig unwrapLoadBalancingConfig(Map<String, ?> lbConfig) {
if (lbConfig.size() != 1) {
throw new RuntimeException(
"There are " + lbConfig.size() + " fields in a LoadBalancingConfig object. Exactly one"
+ " is expected. Config=" + lbConfig);
}
String key = lbConfig.entrySet().iterator().next().getKey();
return new LbConfig(key, JsonUtil.getObject(lbConfig, key));
}
/**
* Given a JSON list of LoadBalancingConfigs, and convert it into a list of LbConfig.
*/
public static List<LbConfig> unwrapLoadBalancingConfigList(List<Map<String, ?>> list) {
if (list == null) {
return null;
}
ArrayList<LbConfig> result = new ArrayList<>();
for (Map<String, ?> rawChildPolicy : list) {
result.add(unwrapLoadBalancingConfig(rawChildPolicy));
}View on GitHub (pinned to 64daddc1f3)