mybatis/mybatis-3 · error · TypeException

Unable to find a usable constructor for {}

Error message

Unable to find a usable constructor for {}

What it means

TypeHandlerRegistry.getInstance looks for a public constructor taking java.lang.reflect.Type (or Class) and, failing that, a public no-arg constructor. If the handler class has neither, the NoSuchMethodException is wrapped in this TypeException: the handler cannot be instantiated for a mapped type.

Source

Thrown at src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java:531

      if (javaType != null) {
        try {
          c = handlerClass.getConstructor(Type.class);
          return (TypeHandler<T>) c.newInstance(javaType);
        } catch (NoSuchMethodException ignored) {
        }
        if (javaType instanceof Class) {
          try {
            c = handlerClass.getConstructor(Class.class);
            return (TypeHandler<T>) c.newInstance(javaType);
          } catch (NoSuchMethodException ignored) {
          }
        }
      }
      try {
        c = handlerClass.getConstructor();
        return (TypeHandler<T>) c.newInstance();
      } catch (NoSuchMethodException e) {
        throw new TypeException("Unable to find a usable constructor for " + handlerClass, e);
      }
    } catch (ReflectiveOperationException e) {
      throw new TypeException("Failed to invoke constructor for handler " + handlerClass, e);
    }
  }

  // scan

  public void register(String packageName) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
    Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
    for (Class<?> type : handlerSet) {
      // Ignore inner classes and interfaces (including package-info.java) and abstract classes
      if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
        register(type);
      }
    }

View on GitHub (pinned to 008069adb1)

Solutions

  1. Add a public no-arg constructor to the handler class
  2. Or add a public constructor accepting a single Class/Type argument
  3. Reference the concrete (non-abstract, static) handler class from the mapper/configuration

Example fix

// before
public class MyHandler extends BaseTypeHandler<Json> {
  private MyHandler(String cfg) { ... }
}

// after
public class MyHandler extends BaseTypeHandler<Json> {
  public MyHandler() { this("default"); }
  private MyHandler(String cfg) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

boolean hasUsableCtor = Arrays.stream(handlerClass.getConstructors()).anyMatch(
  c -> c.getParameterCount() == 0
   || (c.getParameterCount() == 1 && (Type.class.equals(c.getParameterTypes()[0]) || Class.class.equals(c.getParameterTypes()[0]))));
if (!hasUsableCtor) throw new IllegalStateException(handlerClass + " needs a public () or (Class) constructor");

Prevention

When it happens

Trigger: A custom handler whose only constructors take other arguments (e.g. MyHandler(String)) or are private; a handler with only a (Class) constructor that is not public; abstract handlers accidentally referenced from a mapper.

Common situations: DI-oriented handler classes designed for container injection (no usable plain constructor); inner non-static handler classes; mapper XML referencing an abstract base handler instead of the concrete subclass.

Related errors


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