grpc/grpc-java · error · UnsupportedOperationException
handleSubchannelState() is not supported by ${this.getClass(
Error message
handleSubchannelState() is not supported by ${this.getClass().getName()} What it means
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.
Source
Thrown at util/src/main/java/io/grpc/util/GracefulSwitchLoadBalancer.java:179
private void swap() {
helper.updateBalancingState(pendingState, pendingPicker);
currentLb.shutdown();
currentLb = pendingLb;
currentBalancerFactory = pendingBalancerFactory;
pendingLb = defaultBalancer;
pendingBalancerFactory = null;
}
@Override
protected LoadBalancer delegate() {
return pendingLb == defaultBalancer ? currentLb : pendingLb;
}
@Override
@Deprecated
public void handleSubchannelState(
Subchannel subchannel, ConnectivityStateInfo stateInfo) {
throw new UnsupportedOperationException(
"handleSubchannelState() is not supported by " + this.getClass().getName());
}
@Override
public void shutdown() {
pendingLb.shutdown();
currentLb.shutdown();
}
public String delegateType() {
return delegate().getClass().getSimpleName();
}
/**
* Provided a JSON list of LoadBalancingConfigs, parse it into a config to pass to GracefulSwitch.
*/
public static ConfigOrError parseLoadBalancingPolicyConfig(
List<Map<String, ?>> loadBalancingConfigs) {View on GitHub (pinned to 64daddc1f3)
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.
Example fix
// before
balancer.handleSubchannelState(subchannel, ConnectivityStateInfo.forTransientFailure(t));
// after
subchannel.start(new SubchannelStateListener() {
public void onSubchannelState(ConnectivityStateInfo stateInfo) {
// handle state changes here instead
}
}); Defensive patterns
Strategy: type-guard
Validate before calling
// caller-side check before invoking
def supportsHandleSubchannelState(lb) {
return !(lb instanceof io.grpc.util.GracefulSwitchLoadBalancer);
} Type guard
boolean canHandleManually(LoadBalancer lb) {
return lb != null && !(lb instanceof GracefulSwitchLoadBalancer);
} Try / catch
try {
balancer.handleSubchannelState(subchannel, stateInfo);
} catch (UnsupportedOperationException e) {
// migrate: register a SubchannelStateListener instead
logger.warn("handleSubchannelState unsupported; use subchannel.start(listener)", e);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Use forPort(int, ServerCredentials) instead
- Use Grpc.newServerBuilderForPort() instead
- Can't set TLS settings for ALTS
- Unsupported operation getPort()
- Not implemented
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/962434ae1a41666a.
Report an issue: GitHub.