grpc/grpc-java · error · XdsInitializationException

Caller failed to initialize XDS_CLIENT_SUPPLIER

Error message

Caller failed to initialize XDS_CLIENT_SUPPLIER

What it means

XdsNameResolver's XdsClient supplier was never initialized: the holder's get() returned null, meaning the caller was expected to set XDS_CLIENT_SUPPLIER (typically via an injected supplier in tests or embedded usage) but did not. This is an internal initialization guard surfaced as XdsInitializationException.

Source

Thrown at xds/src/main/java/io/grpc/xds/XdsNameResolver.java:1121

    @Override
    public XdsClient returnObject(XdsClient xdsClient) {
      return xdsClientPool.returnObject(xdsClient);
    }
  }

  private static final class SupplierXdsClientPool implements XdsClientPool {
    private final Supplier<XdsClient> xdsClientSupplier;

    SupplierXdsClientPool(Supplier<XdsClient> xdsClientSupplier) {
      this.xdsClientSupplier = checkNotNull(xdsClientSupplier, "xdsClientSupplier");
    }

    @Override
    public XdsClient getObject() throws XdsInitializationException {
      XdsClient xdsClient = xdsClientSupplier.get();
      if (xdsClient == null) {
        throw new XdsInitializationException("Caller failed to initialize XDS_CLIENT_SUPPLIER");
      }
      return xdsClient;
    }

    @Override
    public XdsClient returnObject(XdsClient xdsClient) {
      return null;
    }
  }

  static final class RawMessageClientInterceptor implements ClientInterceptor {
    private static final MethodDescriptor.Marshaller<InputStream> RAW_MARSHALLER =
        new MethodDescriptor.Marshaller<InputStream>() {
          @Override
          public InputStream stream(InputStream value) {
            return value;
          }

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Use the standard XdsNameResolverProvider / xDS bootstrapping path instead of constructing XdsNameResolver directly
  2. In tests, set the XDS_CLIENT_SUPPLIER (or inject an XdsClient supplier) before resolving
  3. Check that no double-shading of io.grpc.xds leaves two copies of the class with separate statics

Example fix

// before
XdsNameResolver resolver = new XdsNameResolver(authority, target, ...); // supplier never set
// after
NameResolverResolverProvider provider = new XdsNameResolverProvider();
NameResolver resolver = provider.newNameResolver(targetUri, args);
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure a supplier is registered before resolving
Supplier<XdsClient> supplier = getXdsClientSupplierOrNull();
if (supplier == null || supplier.get() == null) {
  throw new IllegalStateException("XDS_CLIENT_SUPPLIER not initialized");
}

Type guard

static boolean isSupplierReady(Supplier<XdsClient> s) {
  return s != null && s.get() != null;
}

Try / catch

try {
  NameResolver nr = provider.newNameResolver(uri, args);
  nr.start(watcher);
} catch (XdsInitializationException e) {
  log.error("xDS not initialized: {}", e.getMessage());
}

Prevention

When it happens

Trigger: Constructing/starting XdsNameResolver (or the XdsResolver it delegates to) where the static/injected XDS_CLIENT_SUPPLIER was never set, so getObject() in the pool returns null at XdsNameResolver.java:1121.

Common situations: Unit tests instantiating the resolver without registering a supplier; embedding gRPC xDS internals directly instead of going through nameResolverProvider; classloader/shading issues leaving the static field unset.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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