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

  1. Un-final the result class (and its methods) so Javassist can subclass it — remove 'final' from the class declaration
  2. Add a public no-arg constructor to the result class
  3. On classloader-heavy apps, disable lazy loading or reuse a single classloader to avoid re-enhancement LinkageErrors
  4. 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

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


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/417cba677147a3a9. Report an issue: GitHub.