mybatis/mybatis-3 · error · IllegalArgumentException
'{}' does not implement TypeHandler.
Error message
'{}' does not implement TypeHandler. What it means
Before scanning a handler class's constructors, TypeHandlerRegistry.register verifies TypeHandler.class.isAssignableFrom(handlerClass). A class that does not implement the interface is rejected immediately with this IllegalArgumentException naming the class.
Source
Thrown at src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java:467
@Deprecated(since = "3.6.0", forRemoval = true)
public void register(String javaTypeClassName, String typeHandlerClassName) throws ClassNotFoundException {
register(Resources.classForName(javaTypeClassName), Resources.classForName(typeHandlerClassName));
}
public void register(Type mappedJavaType, Class<?> handlerClass) {
register(new Type[] { mappedJavaType }, mappedJdbcTypes(handlerClass), handlerClass);
}
// java type + jdbc type + handler type
public void register(Type mappedJavaType, JdbcType mappedJdbcType, Class<?> handlerClass) {
register(new Type[] { mappedJavaType }, new JdbcType[] { mappedJdbcType }, handlerClass);
}
private void register(Type[] mappedJavaTypes, JdbcType[] mappedJdbcTypes, Class<?> handlerClass) {
if (!TypeHandler.class.isAssignableFrom(handlerClass)) {
throw new IllegalArgumentException(String.format("'%s' does not implement TypeHandler.", handlerClass.getName()));
}
for (Constructor<?> constructor : handlerClass.getConstructors()) {
if (constructor.getParameterCount() != 1) {
continue;
}
Class<?> argType = constructor.getParameterTypes()[0];
if (Type.class.equals(argType) || Class.class.equals(argType)) {
for (Type javaType : mappedJavaTypes) {
smartHandlers.computeIfAbsent(javaType, k -> constructor);
}
return;
}
}
// It is not a smart handler
register(mappedJavaTypes, mappedJdbcTypes, getInstance(null, handlerClass));
}
private Type[] mappedJavaTypes(Class<?> clazz) {View on GitHub (pinned to 008069adb1)
Solutions
- Make the class implement org.apache.ibatis.type.TypeHandler (usually by extending BaseTypeHandler<T>)
- Fix the handler attribute to reference the actual handler class
- If an alias is used for the handler, verify it resolves to the intended class
Example fix
// before
public class MoneyMapper { ... }
// after
public class MoneyTypeHandler extends BaseTypeHandler<Money> { ... } Defensive patterns
Strategy: type-guard
Validate before calling
if (!TypeHandler.class.isAssignableFrom(handlerClass)) {
throw new IllegalArgumentException(handlerClass.getName() + " is not a TypeHandler");
} Type guard
static boolean isTypeHandler(Class<?> c) {
return c != null && TypeHandler.class.isAssignableFrom(c);
} Prevention
- Make handlers extend BaseTypeHandler<T> so the interface is guaranteed
- CI check: every class named in handler= implements TypeHandler
When it happens
Trigger: <typeHandler handler="com.example.NotAHandler"/> where the class is a plain utility/mapper class; passing a type-alias-resolved class that points to the wrong type; copy-paste of a handler registration with an outdated class name.
Common situations: Refactoring that renamed the real handler and left an old class in place; alias collisions resolving handler= to a different class; pointing handler= at an interceptor (org.apache.ibatis.plugin.Interceptor) instead of a TypeHandler.
Related errors
- {} does not represent an enum type.
- Failed to invoke constructor {}
- Type {typeHandlerType} is not a valid TypeHandler because it
- SqlRunner could not find a TypeHandler instance for {}
- Could not find type handler for Java type '" + propertyGener
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/6cbd02ad94c5dc26.
Report an issue: GitHub.