grpc/grpc-java · error · RuntimeException

Storage override failed to initialize

Error message

Storage override failed to initialize

What it means

When a custom io.grpc.Context.Storage class is configured via the org.grpc.Context.storageClassName system property, Context.createStorage attempts to load and instantiate it reflectively. If the class is found but cannot be constructed or initialized (e.g. its constructor throws, it is abstract, lacks a no-arg constructor, or a static initializer fails), the exception is wrapped in a RuntimeException with this message. Note the distinct case: a ClassNotFoundException is NOT wrapped — gRPC silently falls back to ThreadLocalContextStorage.

Source

Thrown at api/src/context/java/io/grpc/Context.java:138

      Throwable failure = deferredStorageFailure.get();
      // Logging must happen after storage has been set, as loggers may use Context.
      if (failure != null) {
        log.log(Level.FINE, "Storage override doesn't exist. Using default", failure);
      }
    }

    private static Storage createStorage(
        AtomicReference<? super ClassNotFoundException> deferredStorageFailure) {
      try {
        Class<?> clazz = Class.forName("io.grpc.override.ContextStorageOverride");
        // The override's constructor is prohibited from triggering any code that can loop back to
        // Context
        return clazz.asSubclass(Storage.class).getConstructor().newInstance();
      } catch (ClassNotFoundException e) {
        deferredStorageFailure.set(e);
        return new ThreadLocalContextStorage();
      } catch (Exception e) {
        throw new RuntimeException("Storage override failed to initialize", e);
      }
    }
  }

  /**
   * Create a {@link Key} with the given debug name.
   *
   * @param debugString a name intended for debugging purposes and does not impact behavior.
   *                    Multiple different keys may have the same debugString.
   *                    The value should be not null.
   */
  public static <T> Key<T> key(String debugString) {
    return new Key<>(debugString);
  }

  /**
   * Create a {@link Key} with the given debug name and default value.
   *

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Read the cause chain (e.getCause()) to find the real failure in the storage class constructor or static initializer and fix it
  2. Verify the configured class has a public no-argument constructor and is a concrete subclass of io.grpc.Context.Storage
  3. Confirm the fully qualified class name in the io.grpc.Context.storageClassName system property matches a class on the classpath of the final artifact (not just test classpath)
  4. If you only meant to disable the default, remove the system property and let gRPC choose the best available storage

Example fix

// before
-Dio.grpc.Context.storageClassName=com.example.MyStorage  // MyStorage has no public no-arg ctor
// after
public class MyStorage implements Context.Storage {
  public MyStorage() {} // public no-arg constructor required
  ...
}
Defensive patterns

Strategy: validation

Validate before calling

String cls = System.getProperty("io.grpc.Context.storageClassName");
if (cls != null) {
  Class<?> c = Class.forName(cls); // may throw ClassNotFoundException -> fallback, not error
  if (java.lang.reflect.Modifier.isAbstract(c.getModifiers()))
    throw new IllegalStateException("storage class " + cls + " is abstract");
  try {
    c.getConstructor().newInstance(); // fail fast at startup
  } catch (ReflectiveOperationException e) {
    throw new IllegalStateException("storage class " + cls + " not instantiable", e);
  }
}

Type guard

boolean isValidStorage(Class<?> c) {
  return Context.Storage.class.isAssignableFrom(c)
      && !java.lang.reflect.Modifier.isAbstract(c.getModifiers())
      && java.util.Arrays.stream(c.getConstructors()).anyMatch(ctor -> ctor.getParameterCount() == 0);
}

Try / catch

try {
  useContextApi();
} catch (RuntimeException e) {
  if ("Storage override failed to initialize".equals(e.getMessage())) {
    log.error("Bad io.grpc.Context.storageClassName; cause:", e.getCause());
    System.clearProperty("io.grpc.Context.storageClassName"); // fall back to default storage
  } else throw e;
}

Prevention

When it happens

Trigger: Calling any Context API (Context.current(), context.attach(), etc.) during first use of Context storage when the system property io.grpc.Context.storageClassName points to a class that loads but cannot be instantiated: missing public no-arg constructor, abstract class, constructor throwing an exception, or a failing static initializer. A ClassNotFoundException instead yields a silent fallback, not this error.

Common situations: Configuring a custom storage class in JVM system properties (e.g. for threadless executors or custom propagation frameworks like instrumented environments); typos or refactoring that renamed the class but left the property stale; packaging the storage class in a shaded jar with the constructor made private; class initialization ordering failures.

Related errors


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