mybatis/mybatis-3 · error · ExecutorException
Error creating lazy proxy. Cause: " + e
Error message
Error creating lazy proxy. Cause: " + e
What it means
JavassistProxyFactory.createEnhancedInstance calls enhancer.create(typesArray, valuesArray) to build the lazy-loading subclass of your domain object; any exception from Javassist during bytecode generation/instantiation is wrapped in this ExecutorException ('Error creating lazy proxy. Cause: ...'). Common underlying causes are final classes/methods that cannot be enhanced, constructor mismatch, or classloader issues in hot-deploy environments.
Source
Thrown at src/main/java/org/apache/ibatis/executor/loader/javassist/JavassistProxyFactory.java:97
try {
type.getDeclaredMethod(WRITE_REPLACE_METHOD);
// ObjectOutputStream will call writeReplace of objects returned by writeReplace
if (LogHolder.log.isDebugEnabled()) {
LogHolder.log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
}
} catch (NoSuchMethodException e) {
enhancer.setInterfaces(new Class[] { WriteReplaceInterface.class });
} catch (SecurityException e) {
// nothing to do here
}
Object enhanced;
Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
try {
enhanced = enhancer.create(typesArray, valuesArray);
} catch (Exception e) {
throw new ExecutorException("Error creating lazy proxy. Cause: " + e, e);
}
((Proxy) enhanced).setHandler(callback);
return enhanced;
}
private static class EnhancedResultObjectProxyImpl implements MethodHandler {
private final Class<?> type;
private final ResultLoaderMap lazyLoader;
private final boolean aggressive;
private final Set<String> lazyLoadTriggerMethods;
private final ObjectFactory objectFactory;
private final List<Class<?>> constructorArgTypes;
private final List<Object> constructorArgs;
private final ReentrantLock lock = new ReentrantLock();
private EnhancedResultObjectProxyImpl(Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration,
ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
this.type = type;View on GitHub (pinned to 008069adb1)
Solutions
- Un-final the result class (and its methods) so Javassist can subclass it — remove 'final' from the class declaration
- Add a public no-arg constructor to the result class
- On classloader-heavy apps, disable lazy loading or reuse a single classloader to avoid re-enhancement LinkageErrors
- Check the nested 'Cause:' in the message — it identifies the exact Javassist failure (final method, abstract class, etc.)
Example fix
// before
public final class Order { // cannot be enhanced
public Order(Long id) { ... } // no no-arg ctor
}
// after
public class Order {
public Order() {}
public Order(Long id) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
// Guard result classes used with lazy loading: subclassable + instantiable
Class<?> c = Order.class;
int mods = c.getModifiers();
if (Modifier.isFinal(mods)) throw new IllegalStateException(c + " must not be final for lazy loading");
try { c.getDeclaredConstructor().newInstance(); } catch (ReflectiveOperationException e) { throw new IllegalStateException(c + " needs a public no-arg constructor", e); } Type guard
static boolean isLazyProxyable(Class<?> c) { return !Modifier.isFinal(c.getModifiers()) && !Modifier.isAbstract(c.getModifiers()); } Try / catch
try { session.selectList("sel.orders"); } catch (ExecutorException e) { if (e.getMessage() != null && e.getMessage().contains("Error creating lazy proxy")) { log.error("Result class not proxyable: " + e.getCause()); } throw e; } Prevention
- Keep lazy-loaded entity classes non-final with a no-arg constructor (or open in Kotlin)
- Read the nested Cause to find the exact enhancement failure
- Avoid hot-redeploy loops that leak enhanced classes
When it happens
Trigger: lazyLoadingEnabled=true and a result class that Javassist cannot subclass: declared final, or whose constructor signature does not match the recorded constructorArgTypes/constructorArgs; also classloader leaks after repeated redeploys where the enhanced class cannot be defined.
Common situations: Kotlin data classes or Java classes marked final (Java 17+ strongly encourages final); domain classes without a no-arg (or matching) constructor; Tomcat hot redeploy producing LinkageError inside enhancer.create; abstract types used as resultType.
Related errors
- Cannot enable lazy loading because Javassist is not availabl
- Error getting mapper instance. Cause: {cause}
- An attempt has been made to read a not loaded lazy property
- Cannot lazy load property [" + this.property + "] of deseria
- Cannot get Configuration as configuration factory was not se
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/417cba677147a3a9.
Report an issue: GitHub.