mybatis/mybatis-3 · critical · IllegalStateException
There is no 'privateLookupIn(Class, Lookup)' method in java.
Error message
There is no 'privateLookupIn(Class, Lookup)' method in java.lang.invoke.MethodHandles.
What it means
MapperProxy's static initializer reflectively looks up MethodHandles.privateLookupIn, which exists only since Java 9. On an older JVM the lookup fails and this IllegalStateException is thrown from the class-init block, meaning MyBatis 3.5.x requires Java 9+ (this reflects the default method invocation support for mapper interfaces with default methods).
Source
Thrown at src/main/java/org/apache/ibatis/binding/MapperProxy.java:53
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -4724728412955527868L;
private static final Method privateLookupInMethod;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
static {
try {
privateLookupInMethod = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"There is no 'privateLookupIn(Class, Lookup)' method in java.lang.invoke.MethodHandles.", e);
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {View on GitHub (pinned to 008069adb1)
Solutions
- Upgrade the runtime to Java 9 or newer (the supported JDK for modern MyBatis)
- Or downgrade MyBatis to a version matching your JVM (e.g. 3.4.x line for Java 8)
- Verify no classpath shadowing puts an old mybatis jar in front of the intended one
Example fix
# before FROM openjdk:8-jre # after FROM openjdk:17-jre
Defensive patterns
Strategy: validation
Validate before calling
if (!System.getProperty("java.version").matches("(9|[1-9][0-9]).*")) {
throw new IllegalStateException("MyBatis 3.5.6+ requires Java 9+");
} Prevention
- Pin Docker/app-server images to Java 9+ (17 recommended) before upgrading MyBatis
- Add an enforcer rule (requireJavaVersion) in the build matching the runtime
When it happens
Trigger: Running a MyBatis version that uses privateLookupIn (3.5.6+ era) on Java 8 or earlier; a build compiled for a newer JDK deployed onto an old JRE (e.g. app server with bundled Java 8).
Common situations: Upgrading MyBatis without upgrading the runtime; Docker images pinned to java:8-jre; legacy application servers (WebLogic/WebSphere on Java 8) with a newer mybatis jar on the classpath.
Related errors
- No environment specified.
- Environment requires an id attribute.
- Failed to invoke 'Class.isRecord()'.
- Unknown execution method for: {name}
- Mapper method '{name}' attempted to return null from a metho
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/6b28a1787f29c85d.
Report an issue: GitHub.