mybatis/mybatis-3 · error · IllegalArgumentException

The 2nd arg must be Class or ParameterizedType, but was: {}

Error message

The 2nd arg must be Class or ParameterizedType, but was: {}

What it means

TypeParameterResolver.resolveTypeVar requires the source type (the class the member was declared against) to be a plain Class or a ParameterizedType. Any other Type implementation — a TypeVariable, WildcardType, or GenericArrayType — is rejected with IllegalArgumentException.

Source

Thrown at src/main/java/org/apache/ibatis/reflection/TypeParameterResolver.java:161

  private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
    Type result;
    Class<?> clazz;
    if (srcType instanceof Class) {
      clazz = (Class<?>) srcType;
    } else if (srcType instanceof ParameterizedType) {
      ParameterizedType parameterizedType = (ParameterizedType) srcType;
      clazz = (Class<?>) parameterizedType.getRawType();
      if (clazz == declaringClass) {
        TypeVariable<?>[] typeVars = declaringClass.getTypeParameters();
        for (int i = 0; i < typeVars.length; i++) {
          if (typeVar.equals(typeVars[i])) {
            return parameterizedType.getActualTypeArguments()[i];
          }
        }
      }
    } else {
      throw new IllegalArgumentException(
          "The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
    }

    if (clazz == declaringClass) {
      Type[] bounds = typeVar.getBounds();
      if (bounds.length > 0) {
        return bounds[0];
      }
      return Object.class;
    }

    Type superclass = clazz.getGenericSuperclass();
    result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
    if (result != null) {
      return result;
    }

    Type[] superInterfaces = clazz.getGenericInterfaces();

View on GitHub (pinned to 008069adb1)

Solutions

  1. Supply concrete type arguments: class MyMapper extends BaseMapper<User> instead of raw BaseMapper.
  2. If you call TypeParameterResolver directly, materialize ParameterizedTypes (e.g. via TypeToken/ParameterizedTypeImpl) before passing srcType.
  3. Simplify the generic signature at the failing boundary so the declaring type is a Class or fully parameterized type.

Example fix

// before
class UserDao extends BaseDao { ... }  // raw, T unresolved
// after
class UserDao extends BaseDao<User> { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(srcType instanceof Class) && !(srcType instanceof ParameterizedType)) {
  throw new IllegalArgumentException("resolve srcType to Class/ParameterizedType first");
}

Type guard

boolean resolvableType(Type t) {
  return t instanceof Class || t instanceof ParameterizedType;
}

Prevention

When it happens

Trigger: Resolving a member whose declaring 'class' is itself unresolved: e.g. subclass hierarchies where getGenericSuperclass() returns a TypeVariable or a generic array because the concrete type arguments were never supplied (raw subclass of a generic base).

Common situations: Raw types: class MyMapper extends BaseMapper without <T> argument; wildcard/generic-array boundaries in deeply generic mappers or type handlers; custom reflection tooling calling TypeParameterResolver directly with unresolved Types.

Related errors


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