grpc/grpc-java · error · NullPointerException

Factory returned null interceptor

Error message

Factory returned null interceptor: ${factory}

What it means

When a ClientInterceptor registered via ManagedChannelImplBuilder wraps an InterceptorFactory, the factory is invoked with the resolved target to produce the actual interceptor. If the factory returns null, the builder throws a NullPointerException with this message identifying the factory, because a null interceptor would silently drop interception.

Solutions

  1. Fix the factory to return a non-null interceptor (a no-op ClientInterceptor) for unsupported targets.
  2. If the factory cannot operate, have it throw a descriptive exception instead of returning null.
  3. Inspect the factory named in the message and add logging inside newInterceptor() to find the null-return path.

Example fix

// before
public ClientInterceptor newInterceptor(String target) {
  return enabled ? new MyInterceptor() : null;
}
// after
public ClientInterceptor newInterceptor(String target) {
  return enabled ? new MyInterceptor() : ClientInterceptors.noop(new CallOptions() {}); // or unconditional interceptor
}
Defensive patterns

Strategy: try-catch

Validate before calling

ClientInterceptorFactory f = ...;
if (f.newInterceptor(target) == null) {
  throw new IllegalStateException("factory " + f + " returns null for target " + target);
}

Type guard

ClientInterceptor safeNew(InterceptorFactory f, String target) {
  ClientInterceptor ci = f.newInterceptor(target);
  return ci != null ? ci : (call, next) -> next.startCall(call);
}

Try / catch

try {
  builder.intercept(factoryWrapper).build();
} catch (NullPointerException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("Factory returned null interceptor")) {
    log.error("Fix interceptor factory: {}", e.getMessage());
  } else throw e;
}

Prevention

When it happens

Trigger: Registering an interceptor whose InterceptorFactory.newInterceptor(computedTarget) returns null — e.g. a factory that disables itself for certain targets by returning null instead of a no-op interceptor.

Common situations: Custom interceptor factories conditionally skipping targets; third-party instrumentation factories that fail to construct (returning null on missing config) rather than throwing.

Related errors


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

Appendix: source

Thrown at core/src/main/java/io/grpc/internal/ManagedChannelImplBuilder.java:806

        new ExponentialBackoffPolicy.Provider(),
        SharedResourcePool.forResource(GrpcUtil.SHARED_CHANNEL_EXECUTOR),
        GrpcUtil.STOPWATCH_SUPPLIER,
        getEffectiveInterceptors(resolvedResolver.targetUri.toString()),
        TimeProvider.SYSTEM_TIME_PROVIDER));
  }

  // Temporarily disable retry when stats or tracing is enabled to avoid breakage, until we know
  // what should be the desired behavior for retry + stats/tracing.
  // TODO(zdapeng): FIX IT
  @VisibleForTesting
  List<ClientInterceptor> getEffectiveInterceptors(String computedTarget) {
    List<ClientInterceptor> effectiveInterceptors = new ArrayList<>(this.interceptors.size());
    for (ClientInterceptor interceptor : this.interceptors) {
      if (interceptor instanceof InterceptorFactoryWrapper) {
        InterceptorFactory factory = ((InterceptorFactoryWrapper) interceptor).factory;
        interceptor = factory.newInterceptor(computedTarget);
        if (interceptor == null) {
          throw new NullPointerException("Factory returned null interceptor: " + factory);
        }
      }
      effectiveInterceptors.add(interceptor);
    }

    boolean disableImplicitCensus = InternalConfiguratorRegistry.wasSetConfiguratorsCalled();
    if (disableImplicitCensus) {
      return effectiveInterceptors;
    }
    if (statsEnabled) {
      ClientInterceptor statsInterceptor = null;

      if (GET_CLIENT_INTERCEPTOR_METHOD != null) {
        try {
          statsInterceptor =
              (ClientInterceptor) GET_CLIENT_INTERCEPTOR_METHOD
              .invoke(
                null,

View on GitHub (pinned to 64daddc1f3)