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
- Read the cause chain (e.getCause()) to find the real failure in the storage class constructor or static initializer and fix it
- Verify the configured class has a public no-argument constructor and is a concrete subclass of io.grpc.Context.Storage
- 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)
- 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
- Instantiate the custom Storage class once in your own startup code before relying on Context
- Always give custom storage classes a public no-arg constructor
- Keep the fully qualified class name in sync with refactors (refer to the class literal, not a string, where possible)
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
- Failed to create OkHttpChannelBuilder
- Cannot load EpollSocketChannel
- EpollEventLoopGroup constructor not found
- Cannot create Epoll EventLoopGroup
- ChannelOption(${optionName}) is not available
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/6ac87d6461731ee4.
Report an issue: GitHub.