baomidou/mybatis-plus · error · IllegalArgumentException

Type argument must be an enum

Error message

Type argument must be an enum: {}

What it means

EnumCache.createMetadata builds the EnumMetadata (value-to-enum mappings) for a class and throws IllegalArgumentException('Type argument must be an enum: X') when the class is not java.lang.Enum. The metadata cache is only valid for enums; passing an interface, annotation, or ordinary class means value extraction (getEnumConstants, IEnum interface check) cannot proceed.

Solutions

  1. Make the mapped type an actual enum (declare it with the enum keyword) — classes merely implementing IEnum are not supported.
  2. Check the registered type handler for the field: non-enum types must not be handled by the enum type handler chain.
  3. Guard call sites with clazz.isEnum() (or EnumCache.isMpEnums(clazz)) before touching enum metadata APIs.

Example fix

// before: class implements IEnum but is not an enum -> IAE
public class Status implements IEnum<Integer> { ... }

// after: declare it as an enum
public enum Status implements IEnum<Integer> {
    OK(0), FAIL(1);
    private final int code;
    Status(int code) { this.code = code; }
    public Integer getValue() { return code; }
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!clazz.isEnum()) {
    throw new IllegalArgumentException(clazz + " is not an enum; enum handling requires java.lang.Enum");
}

Type guard

static boolean isMpEnum(Class<?> c) {
    return c != null && c.isEnum()
        && (IEnum.class.isAssignableFrom(c) || EnumCache.findEnumValueFieldName(c).isPresent());
}

Try / catch

try {
    EnumCache.metadata(candidate);
} catch (IllegalArgumentException e) {
    // fall back to non-enum handling instead of failing
    handleAsNonEnum(candidate);
}

Prevention

When it happens

Trigger: Cache.computeIfAbsent(enumClassType, EnumCache::createMetadata) runs with a non-enum class — e.g. code path determined a field 'is an mp enum' incorrectly, or a custom type handler invoked EnumCache lookups on a plain class. Also reachable when an enum-like class (abstract class implementing IEnum without being an enum) is routed to the enum cache.

Common situations: A class implements IEnum but is not declared as enum; the wrong type handler is registered for a field so enum machinery runs against a normal class; reflective code feeds raw Class objects into enum caching utilities.

Related errors


AI-assisted analysis of baomidou/mybatis-plus@bf67d90747 (2026-08-14). Data as JSON: /api/errors/31fb5dce4229bafa. Report an issue: GitHub.

Appendix: source

Thrown at mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/EnumCache.java:73

        }
        return CACHE.computeIfAbsent(enumClassType, EnumCache::createMetadata);
    }

    static Optional<String> findEnumValueFieldName(Class<?> clazz) {
        if (clazz == null || !clazz.isEnum()) {
            return Optional.empty();
        }
        String fieldName = ENUM_VALUE_FIELD_CACHE.computeIfAbsent(clazz, EnumCache::findEnumValueFieldNameOrEmpty);
        return NO_ENUM_VALUE_FIELD.equals(fieldName) ? Optional.empty() : Optional.of(fieldName);
    }

    static boolean isMpEnums(Class<?> clazz) {
        return clazz != null && clazz.isEnum() && (IEnum.class.isAssignableFrom(clazz) || findEnumValueFieldName(clazz).isPresent());
    }

    private static EnumMetadata createMetadata(Class<?> enumClassType) {
        if (!enumClassType.isEnum()) {
            throw new IllegalArgumentException("Type argument must be an enum: " + enumClassType.getName());
        }
        MetaClass metaClass = MetaClass.forClass(enumClassType, REFLECTOR_FACTORY);
        String getterName = resolveGetterName(enumClassType);
        Class<?> propertyType = ReflectionKit.resolvePrimitiveIfNecessary(metaClass.getGetterType(getterName));
        Invoker getInvoker = metaClass.getGetInvoker(getterName);
        Enum<?>[] enumConstants = (Enum<?>[]) enumClassType.getEnumConstants();
        Map<Object, Enum<?>> valueToEnum = new HashMap<>(enumConstants.length * 4 + 1);
        Map<Enum<?>, Object> enumToValue = new IdentityHashMap<>(enumConstants.length);
        for (Enum<?> enumConstant : enumConstants) {
            Object value = invokeValue(getInvoker, enumConstant);
            enumToValue.put(enumConstant, value);
            EnumUtils.putEnumValue(valueToEnum, value, enumConstant);
        }
        return new EnumMetadata(propertyType, enumConstants, Collections.unmodifiableMap(valueToEnum),
            Collections.unmodifiableMap(enumToValue), getterName);
    }

    private static String resolveGetterName(Class<?> enumClassType) {

View on GitHub (pinned to bf67d90747)