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

  1. Check the cause of the ExceptionInInitializerError (e.getCause() is the ReflectiveOperationException) to see exactly which lookup was denied
  2. 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)
  3. Ensure generated classes are compiled into the same package/module as the classes whose fields they access with VarHandles
  4. 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

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


AI-assisted analysis of ben-manes/caffeine@9da6581ee3 (2026-08-14). Data as JSON: /api/errors/a66b5e28540931b8. Report an issue: GitHub.