grpc/grpc-java · error · IllegalStateException

Resolution is pending

Error message

Resolution is pending

What it means

ManagedChannelImpl installs INITIAL_PENDING_SELECTOR as a placeholder InternalConfigSelector before the first resolution completes. If any code attempts to select a service config via this placeholder before resolution finishes, it throws IllegalStateException("Resolution is pending") — this selector is only expected to be consulted when channel state bookkeeping is wrong.

Solutions

  1. Wait for the channel to become READY (or for onResolved callbacks) before consulting the config selector.
  2. Register a LoadBalancer/Subchannel listener and only use config selection after resolution state transitions.
  3. If this occurs in normal operation, file a bug — the placeholder is not meant to be reachable.

Example fix

// before
ConfigSelector cs = channel.getConfigSelector();
Result r = cs.selectConfig(args); // may throw
// after
channel.getState(true); // trigger resolution
// use ConfigSelector only from LoadBalancer callbacks after resolution completes
Defensive patterns

Strategy: try-catch

Validate before calling

// Guard: only use config selection after channel state leaves IDLE/connecting
boolean ready = channel.getState(false) == io.grpc.ConnectivityState.READY;

Type guard

InternalConfigSelector sel = channel.getConfigSelector();
if (sel == null) return null; // not yet resolved

Try / catch

try {
  return selector.selectConfig(args);
} catch (IllegalStateException e) {
  if ("Resolution is pending".equals(e.getMessage())) return defaultConfig(args);
  throw e;
}

Prevention

When it happens

Trigger: Calling selectConfig on the channel's InternalConfigSelector while the NameResolver has not yet returned its first result, typically via internal APIs or ConfigSelector usage racing with channel startup.

Common situations: Custom balancers or internal tooling reading the config selector immediately after channel creation, before name resolution completes; race conditions between resolution start and config lookup.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/44068e537a3973e5. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/io/grpc/internal/ManagedChannelImpl.java:153

  @VisibleForTesting
  static final Status SHUTDOWN_NOW_STATUS =
      Status.UNAVAILABLE.withDescription("Channel shutdownNow invoked");

  @VisibleForTesting
  static final Status SHUTDOWN_STATUS =
      Status.UNAVAILABLE.withDescription("Channel shutdown invoked");

  @VisibleForTesting
  static final Status SUBCHANNEL_SHUTDOWN_STATUS =
      Status.UNAVAILABLE.withDescription("Subchannel shutdown invoked");

  private static final ManagedChannelServiceConfig EMPTY_SERVICE_CONFIG =
      ManagedChannelServiceConfig.empty();
  private static final InternalConfigSelector INITIAL_PENDING_SELECTOR =
      new InternalConfigSelector() {
        @Override
        public Result selectConfig(PickSubchannelArgs args) {
          throw new IllegalStateException("Resolution is pending");
        }
      };
  private static final LoadBalancer.PickDetailsConsumer NOOP_PICK_DETAILS_CONSUMER =
      new LoadBalancer.PickDetailsConsumer() {};

  /**
   * Retrieves the user-provided configuration function for internal child channels.
   *
   * <p>This is intended for use by gRPC internal components
   * that are responsible for creating auxiliary {@code ManagedChannel} instances.
   */
  private final ChannelConfigurator channelConfigurator;

  private final InternalLogId logId;
  private final String target;
  @Nullable
  private final String authorityOverride;
  private final NameResolverRegistry nameResolverRegistry;

View on GitHub (pinned to 64daddc1f3)