{"record":{"id":"962434ae1a41666a","repo":"grpc/grpc-java","slug":"handlesubchannelstate-is-not-supported-by-this","errorCode":null,"errorMessage":"handleSubchannelState() is not supported by ${this.getClass().getName()}","messagePattern":"handleSubchannelState\\(\\) is not supported by (.+?)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"util/src/main/java/io/grpc/util/GracefulSwitchLoadBalancer.java","lineNumber":179,"sourceCode":"  private void swap() {\n    helper.updateBalancingState(pendingState, pendingPicker);\n    currentLb.shutdown();\n    currentLb = pendingLb;\n    currentBalancerFactory = pendingBalancerFactory;\n    pendingLb = defaultBalancer;\n    pendingBalancerFactory = null;\n  }\n\n  @Override\n  protected LoadBalancer delegate() {\n    return pendingLb == defaultBalancer ? currentLb : pendingLb;\n  }\n\n  @Override\n  @Deprecated\n  public void handleSubchannelState(\n      Subchannel subchannel, ConnectivityStateInfo stateInfo) {\n    throw new UnsupportedOperationException(\n        \"handleSubchannelState() is not supported by \" + this.getClass().getName());\n  }\n\n  @Override\n  public void shutdown() {\n    pendingLb.shutdown();\n    currentLb.shutdown();\n  }\n\n  public String delegateType() {\n    return delegate().getClass().getSimpleName();\n  }\n\n  /**\n   * Provided a JSON list of LoadBalancingConfigs, parse it into a config to pass to GracefulSwitch.\n   */\n  public static ConfigOrError parseLoadBalancingPolicyConfig(\n      List<Map<String, ?>> loadBalancingConfigs) {","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/util/src/main/java/io/grpc/util/GracefulSwitchLoadBalancer.java#L161-L197","documentation":"GracefulSwitchLoadBalancer inherits the deprecated handleSubchannelState() entry point from LoadBalancer, but the gRPC team removed support for it because subchannel state now flows through the balancing helper's updateBalancingState / subchannel callbacks. Calling this method on this balancer always throws UnsupportedOperationException; it is a hard API-contract violation, not a runtime failure of the balancer itself.","triggerScenarios":"A custom LoadBalancer or old client-side code explicitly calls handleSubchannelState(subchannel, stateInfo) on a GracefulSwitchLoadBalancer (or a balancer wrapped by it), typically code written against the pre-2.x LoadBalancer API that relied on manual subchannel state propagation.","commonSituations":"Upgrading gRPC-java while keeping an old custom load balancer or interceptor that still invokes the deprecated handleSubchannelState hook; copy-pasted balancer code from older tutorials; third-party balancer implementations delegating state to a graceful-switch wrapper.","solutions":["Stop calling handleSubchannelState(); rely on the Subchannel's state delivered via the LoadBalancer's Helper (SubchannelStateListener / start(Listener) API).","If you maintain a custom balancer, migrate to the newer API: subchannel.start(SubchannelStateListener) and handle state in that listener.","Wrap the call defensively only during migration, but plan to remove it since the method is deprecated and always throws.","Check third-party load-balancer libraries for versions compatible with your gRPC-java release and upgrade them."],"exampleFix":"// before\nbalancer.handleSubchannelState(subchannel, ConnectivityStateInfo.forTransientFailure(t));\n// after\nsubchannel.start(new SubchannelStateListener() {\n  public void onSubchannelState(ConnectivityStateInfo stateInfo) {\n    // handle state changes here instead\n  }\n});","handlingStrategy":"type-guard","validationCode":"// caller-side check before invoking\ndef supportsHandleSubchannelState(lb) {\n  return !(lb instanceof io.grpc.util.GracefulSwitchLoadBalancer);\n}","typeGuard":"boolean canHandleManually(LoadBalancer lb) {\n  return lb != null && !(lb instanceof GracefulSwitchLoadBalancer);\n}","tryCatchPattern":"try {\n  balancer.handleSubchannelState(subchannel, stateInfo);\n} catch (UnsupportedOperationException e) {\n  // migrate: register a SubchannelStateListener instead\n  logger.warn(\"handleSubchannelState unsupported; use subchannel.start(listener)\", e);\n}","preventionTips":["Never call the deprecated handleSubchannelState(); use subchannel.start(SubchannelStateListener).","Audit custom LoadBalancer implementations when upgrading gRPC-java versions.","Keep balancer code aligned with the current LoadBalancer API docs.","Pin compatible versions of third-party load-balancer extensions."],"tags":["grpc","load-balancer","unsupported-operation","deprecated-api"],"backgroundTag":"unsupported-operation","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"}