mybatis/mybatis-3 · error · TypeException
Failed to invoke constructor {}
Error message
Failed to invoke constructor {} What it means
When a type handler is instantiated on demand, TypeHandlerRegistry calls candidate.newInstance(type) through reflection. Any ReflectiveOperationException — including an exception thrown INSIDE the constructor, such as EnumTypeHandler's 'Type argument cannot be null' — surfaces as this TypeException naming the handler class.
Source
Thrown at src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java:328
if (candidate == null) {
if (type instanceof Class) {
Class<?> clazz = (Class<?>) type;
if (Enum.class.isAssignableFrom(clazz)) {
Class<?> enumClass = (clazz.isAnonymousClass() || !clazz.isEnum()) ? clazz.getSuperclass() : clazz;
TypeHandler<?> enumHandler = getInstance(enumClass, defaultEnumTypeHandler);
register(new Type[] { enumClass }, new JdbcType[] { jdbcType }, enumHandler);
return enumHandler;
}
}
return null;
}
try {
TypeHandler<?> typeHandler = (TypeHandler<?>) candidate.newInstance(type);
register(type, jdbcType, typeHandler);
return typeHandler;
} catch (ReflectiveOperationException e) {
throw new TypeException("Failed to invoke constructor " + candidate.toString(), e);
}
}
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = typeHandlerMap.get(type);
if (jdbcHandlerMap != null) {
return NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap) ? null : jdbcHandlerMap;
}
if (type instanceof Class) {
Class<?> clazz = (Class<?>) type;
if (!Enum.class.isAssignableFrom(clazz)) {
jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
}
}
typeHandlerMap.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
return jdbcHandlerMap;
}
View on GitHub (pinned to 008069adb1)
Solutions
- Read the cause: if it is another exception, the constructor itself threw — fix that root cause (usually a null/unresolvable java type)
- Give the handler class a public no-arg constructor and a public constructor taking Class
- Register the handler with an explicit javaType so the registry never passes null
Example fix
// before
class MyHandler extends BaseTypeHandler<MyType> {
public MyHandler(Class<?> type) { if (type == null) throw new IllegalStateException(); }
}
// after
class MyHandler extends BaseTypeHandler<MyType> {
public MyHandler() {}
public MyHandler(Class<?> type) { /* tolerate null, default sensibly */ }
} Defensive patterns
Strategy: try-catch
Validate before calling
// verify a usable constructor exists before registering
boolean usable = Arrays.stream(handlerClass.getConstructors())
.anyMatch(c -> c.getParameterCount() == 1 && (Type.class.equals(c.getParameterTypes()[0]) || Class.class.equals(c.getParameterTypes()[0])));
if (!usable && Arrays.stream(handlerClass.getConstructors()).noneMatch(c -> c.getParameterCount() == 0)) {
throw new IllegalStateException(handlerClass + " has no (Class) or () constructor");
} Try / catch
try { registry.getInstance(type, handlerClass); } catch (TypeException e) { // unwrap cause; if constructor logic threw, fix the constructor rather than retrying } Prevention
- Keep handler constructors side-effect free and null-tolerant
- Give every custom handler a public no-arg constructor
- Instantiate handlers once in tests to surface constructor problems before production
When it happens
Trigger: A custom or built-in handler whose Class-arg constructor throws (null java type passed by the registry, unsupported type argument); a handler class with a non-public constructor (IllegalAccessException surfaces here too).
Common situations: Custom handlers that validate their constructor argument and throw for null; registering a handler class whose only constructor is package-private; handler constructors that perform external lookups (JNDI, service) that fail at init.
Related errors
- {} does not represent an enum type.
- '{}' does not implement TypeHandler.
- Unable to find a usable constructor for {}
- Error creating instance. Cause: {cause}
- Type {typeHandlerType} is not a valid TypeHandler because it
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/c2ab775631ba596e.
Report an issue: GitHub.