ben-manes/caffeine · error · ExceptionInInitializerError
throw new ExceptionInInitializerError(e)
Error message
throw new ExceptionInInitializerError(e)
What it means
Caffeine's javaPoet-generated cache node classes initialize their VarHandles in a static initializer block using MethodHandles.lookup(); if that lookup throws a ReflectiveOperationException (access denied, module boundaries, security restrictions), the block wraps it in an ExceptionInInitializerError. This means the generated node class itself failed to initialize; every later use of that class fails with NoClassDefFoundError. It is a class-initialization failure, not a cache logic failure, and always points at reflection permissions or runtime environment problems.
Source
Thrown at caffeine/src/javaPoet/java/com/github/benmanes/caffeine/cache/RuleContext.java:146
.addMember("value", format, suppressedWarnings.toArray())
.build());
}
}
/** Adds the static initializer that binds the class's VarHandles, if any. */
public void addStaticBlock() {
if (varHandles.isEmpty()) {
return;
}
var codeBlock = CodeBlock.builder()
.addStatement("$T lookup = $T.lookup()", LOOKUP, METHOD_HANDLES)
.beginControlFlow("try");
for (var varHandle : varHandles) {
varHandle.accept(codeBlock);
}
codeBlock
.nextControlFlow("catch ($T e)", ReflectiveOperationException.class)
.addStatement("throw new ExceptionInInitializerError(e)")
.endControlFlow();
classSpec.addStaticBlock(codeBlock.build());
}
}
View on GitHub (pinned to 9da6581ee3)
Solutions
- Check the cause of the ExceptionInInitializerError (e.getCause() is the ReflectiveOperationException) to see exactly which lookup was denied
- Run with the required opens, e.g. --add-opens java.base/java.lang=ALL-UNNAMED (or open the package containing the generated node's target fields to the generated class's module)
- Ensure generated classes are compiled into the same package/module as the classes whose fields they access with VarHandles
- Prefer the standard prebuilt Caffeine artifact instead of the javaPoet-generated variant unless you specifically need it
Example fix
# before
java --add-modules=... -jar app.jar # ExceptionInInitializerError at first cache access
# after
java --add-opens java.base/java.lang=ALL-UNNAMED \
--add-opens java.base/java.lang.invoke=ALL-UNNAMED \
-jar app.jar Defensive patterns
Strategy: try-catch
Validate before calling
// Before first cache use, verify VarHandle lookup is permitted in this runtime:
try {
var lookup = MethodHandles.lookup();
lookup.findVarHandle(java.util.concurrent.atomic.AtomicInteger.class, "value", int.class);
System.out.println("VarHandle lookups OK");
} catch (ReflectiveOperationException e) {
System.err.println("Reflective access blocked: " + e);
} Try / catch
try {
Cache<K, V> cache = Caffeine.newBuilder().build();
cache.get(key, this::load); // first use triggers class init
} catch (ExceptionInInitializerError | NoClassDefFoundError e) {
Throwable cause = (e instanceof ExceptionInInitializerError eiie) ? eiie.getException() : e;
throw new IllegalStateException("Generated VarHandle init failed; check --add-opens / JPMS config", cause);
} Prevention
- Run the javaPoet-generated variant in the same module/package layout as the fields it accesses
- Add required --add-opens flags to JVM startup scripts and container entrypoints up front
- Smoke-test cache construction in a startup health check so class-init failures surface at boot, not mid-request
When it happens
Trigger: Instantiating or first using a cache built from the javaPoet source variant, where the generated static block's MethodHandles.Lookup.findVarHandle(...) fails because the generated class cannot access the target fields (IllegalAccessException) or the runtime blocks such lookups (JPMS module rules, SecurityManager, restricted JVM like some Android/legacy runtimes).
Common situations: Running generated Caffeine sources under strict JPMS without opening the owning package to the generated class; a custom build where generated classes live in a different package/module than the fields they access; non-standard JVMs with incomplete MethodHandles support.
Related errors
- An invalid state was detected, occurring when the key's equa
- Proxy required
- Invalid option
- key %s value was set to %s, must be an integer
- key %s value was set to %s, must be a long
AI-assisted analysis of ben-manes/caffeine@9da6581ee3 (2026-08-14).
Data as JSON: /api/errors/a66b5e28540931b8.
Report an issue: GitHub.