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.

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)

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.