quarkusio/quarkus · error · IllegalStateException

invalid configuration for a Stork Load Balancer : ${loadBala

Error message

invalid configuration for a Stork Load Balancer : ${loadBalancerConfig}

What it means

The Stork load balancer for gRPC expects the resolved address's load-balancing policy config object to be an instance of StorkLoadBalancerConfig, which is installed by Quarkus when the Stork name resolver wires the policy. If the object is missing or of a different type, the channel state is inconsistent (typically the Stork LB policy was not properly registered for this channel) and handleResolvedAddresses throws IllegalStateException.

Source

Thrown at extensions/grpc/runtime/src/main/java/io/quarkus/grpc/runtime/stork/GrpcLoadBalancerProvider.java:88

            return NameResolver.ConfigOrError.fromError(Status.INTERNAL);
        }
        return NameResolver.ConfigOrError
                .fromConfig(new StorkLoadBalancerConfig(serviceName));
    }

    @Override
    public LoadBalancer newLoadBalancer(LoadBalancer.Helper helper) {
        return new LoadBalancer() {

            String serviceName;

            @Override
            public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
                List<EquivalentAddressGroup> addresses = resolvedAddresses.getAddresses();

                Object loadBalancerConfig = resolvedAddresses.getLoadBalancingPolicyConfig();
                if (!(loadBalancerConfig instanceof StorkLoadBalancerConfig)) {
                    throw new IllegalStateException("invalid configuration for a Stork Load Balancer : " + loadBalancerConfig);
                }

                StorkLoadBalancerConfig config = (StorkLoadBalancerConfig) loadBalancerConfig;

                Map<ServiceInstance, Subchannel> subChannels = new TreeMap<>(Comparator.comparingLong(ServiceInstance::getId));
                Set<ServiceInstance> activeSubchannels = Collections.newSetFromMap(new ConcurrentHashMap<>());
                AtomicReference<ConnectivityState> state = new AtomicReference<>(ConnectivityState.CONNECTING);

                serviceName = config.serviceName;

                final StorkSubchannelPicker picker = new StorkSubchannelPicker(subChannels, serviceName, activeSubchannels);

                for (EquivalentAddressGroup addressGroup : addresses) {
                    ServiceInstance serviceInstance = addressGroup.getAttributes()
                            .get(GrpcStorkServiceDiscovery.SERVICE_INSTANCE);
                    CreateSubchannelArgs subChannelArgs = CreateSubchannelArgs.newBuilder()
                            .setAddresses(addressGroup)
                            .setAttributes(addressGroup.getAttributes())

View on GitHub (pinned to e1c734241f)

Solutions

  1. Do not set the load-balancing policy manually; rely on quarkus.grpc.clients.<name>.name-resolver=stork and let Quarkus install the Stork policy.
  2. Add/align the io.quarkus:quarkus-stork and smallrye-stork dependencies; avoid mixing incompatible versions.
  3. Ensure the service is defined in Stork (quarkus.stork.<service>.* discovery config) so the resolver attaches a StorkLoadBalancerConfig.
  4. If using a custom NameResolver, make sure it passes a StorkLoadBalancerConfig as the load-balancing policy config.

Example fix

// before (application.properties)
quarkus.grpc.clients.hello.load-balancing-policy=stork

// after
quarkus.grpc.clients.hello.name-resolver=stork
quarkus.stork.hello.service-discovery.type=static
quarkus.stork.hello.service-discovery.address-list=localhost:8084
Defensive patterns

Strategy: validation

Validate before calling

// application.properties sanity check before startup
// do NOT set quarkus.grpc.clients.<name>.load-balancing-policy=stork manually;
// instead set the name resolver:
// quarkus.grpc.clients.<name>.name-resolver=stork
// and define quarkus.stork.<service>.service-discovery.*

Try / catch

try {
    greeter.sayHello(request).await().indefinitely();
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().contains("Stork Load Balancer")) {
        log.error("Channel not built by the Quarkus Stork integration; check name-resolver=stork and quarkus-stork deps");
    } else { throw e; }
}

Prevention

When it happens

Trigger: A gRPC channel is configured with the 'stork' load-balancing policy ('stork' name resolver) but handleResolvedAddresses receives resolvedAddresses.getLoadBalancingPolicyConfig() that is not a StorkLoadBalancerConfig — e.g. the resolver produced addresses without attaching Stork's policy config, or the policy name is set to 'stork' without the Quarkus Stork gRPC integration on the classpath.

Common situations: Manually forcing loadBalancingPolicy=stork in a plain grpc config; mixing Quarkus versions where grpc-stork integration JARs mismatch; using Stork service discovery without quarkus-stork dependency registration so the policy config object is a different type.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/9d41cbb0f6dbdaae. Report an issue: GitHub.