{"record":{"id":"e8199dab33809e4f","repo":"grpc/grpc-java","slug":"maximum-lb-config-recursion-depth-reached","errorCode":null,"errorMessage":"Maximum LB config recursion depth reached","messagePattern":"Maximum LB config recursion depth reached","errorType":"validation","errorClass":"ResourceInvalidException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/LoadBalancerConfigFactory.java","lineNumber":111,"sourceCode":"  static final String ERROR_UTILIZATION_PENALTY = \"errorUtilizationPenalty\";\n  static final String METRIC_NAMES_FOR_COMPUTING_UTILIZATION = \"metricNamesForComputingUtilization\";\n\n  /**\n   * Factory method for creating a new {link LoadBalancerConfigConverter} for a given xDS {@link\n   * Cluster}.\n   *\n   * @throws ResourceInvalidException If the {@link Cluster} has an invalid LB configuration.\n   */\n  static ImmutableMap<String, ?> newConfig(Cluster cluster, boolean enableLeastRequest)\n      throws ResourceInvalidException {\n    // The new load_balancing_policy will always be used if it is set, but for backward\n    // compatibility we will fall back to using the old lb_policy field if the new field is not set.\n    if (cluster.hasLoadBalancingPolicy()) {\n      try {\n        return LoadBalancingPolicyConverter.convertToServiceConfig(cluster.getLoadBalancingPolicy(),\n            0);\n      } catch (MaxRecursionReachedException e) {\n        throw new ResourceInvalidException(\"Maximum LB config recursion depth reached\", e);\n      }\n    } else {\n      return LegacyLoadBalancingPolicyConverter.convertToServiceConfig(cluster, enableLeastRequest);\n    }\n  }\n\n  /**\n   * Builds a service config JSON object for the ring_hash load balancer config based on the given\n   * config values.\n   */\n  private static ImmutableMap<String, ?> buildRingHashConfig(Long minRingSize, Long maxRingSize) {\n    ImmutableMap.Builder<String, Object> configBuilder = ImmutableMap.builder();\n    if (minRingSize != null) {\n      configBuilder.put(MIN_RING_SIZE_FIELD_NAME, minRingSize.doubleValue());\n    }\n    if (maxRingSize != null) {\n      configBuilder.put(MAX_RING_SIZE_FIELD_NAME, maxRingSize.doubleValue());\n    }","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/LoadBalancerConfigFactory.java#L93-L129","documentation":"ResourceInvalidException thrown by LoadBalancerConfigFactory.newConfig when LoadBalancingPolicyConverter.convertToServiceConfig exceeds its recursion limit (MaxRecursionReachedException). Nested LB policy configurations (policies containing policies) are too deeply nested, so the factory rejects the cluster config as invalid to prevent unbounded recursion.","triggerScenarios":"newConfig processes a cluster whose load_balancing_policy contains deeply (or cyclically) nested policy configs — each nested policy increments the depth counter passed to the converter until the max depth is hit.","commonSituations":"Control plane misconfiguration that nests policies excessively or creates a self-referential policy chain; pathological generated configs from a policy-composition tool; Envoy config meant for Envoy's deeper nesting tolerance but rejected by grpc-java's stricter limit.","solutions":["Flatten the LB policy chain on the control plane so nesting depth stays within the converter's limit","Check for and break any self-referential/cyclic policy definitions in the cluster config","Upgrade grpc-xds in case a newer release raised the supported recursion depth","Replace composed custom policies with a single supported policy"],"exampleFix":"// before: deeply nested policy chain\n\"lb_policy\": {\"name\": \"a\", \"config\": {\"policy\": {\"name\": \"b\", \"config\": {\"policy\": {\"name\": \"a\", ...}}}}}\n// after: single flat policy\n\"lb_policy\": {\"name\": \"round_robin\", \"typed_config\": {...}}","handlingStrategy":"try-catch","validationCode":"// bound-check nesting depth of policy configs before sending them\nint depth = 0;\nfor (Node n = root; n != null; n = n.inner()) {\n  if (++depth > 10) throw new IllegalArgumentException(\"LB policy nesting too deep\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  /* start xDS client with cluster */;\n} catch (ResourceInvalidException e) {\n  if (e.getMessage().contains(\"Maximum LB config recursion depth\")) {\n    logger.severe(\"Cluster LB policy nesting too deep: \" + e.getMessage());\n    // fall back to default policy\n  } else { throw e; }\n}","preventionTips":["Keep LB policy composition shallow (1-2 levels)","Detect cyclic policy references in config generation tooling","Test generated Envoy configs against grpc-java limits, not just Envoy's","Prefer a single supported policy over deep custom-policy chains"],"tags":["xds","load-balancing","recursion","config"],"backgroundTag":"invalid-config-value","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}