FasterXML/jackson-databind · error · IllegalArgumentException

Cannot construct EnumMap; generic (key) type not available

Error message

Cannot construct EnumMap; generic (key) type not available

What it means

Thrown by BasicDeserializerFactory when constructing an EnumMap (or a Map<Enum,X> specialized to EnumMap) and the enum key type is not resolvable at runtime (keyType.isEnumImplType() is false). EnumMap requires a concrete enum class to be instantiated; without generic type information Jackson cannot supply it.

Solutions

  1. Provide a fully parameterized type using TypeReference (e.g. new TypeReference<EnumMap<MyEnum,String>>(){}) or constructType with the enum class, so the enum key is reifiable.
  2. Declare the target field/parameter with explicit generic key type (EnumMap<MyEnum, V>), not raw EnumMap.
  3. If the key type is dynamic, build a JavaType via TypeFactory.constructMapType(EnumMap.class, TypeFactory.constructType(enumClass), valueType).

Example fix

// before: raw type, key enum lost
EnumMap map = mapper.readValue(json, EnumMap.class);

// after: fully parameterized
EnumMap<MyEnum,String> map = mapper.readValue(
    json, new TypeReference<EnumMap<MyEnum,String>>(){});
Defensive patterns

Strategy: validation

Validate before calling

JavaType type = mapper.getTypeFactory().constructType(new TypeReference<EnumMap<MyEnum,String>>(){});
if (!type.getKeyType().isEnumImplType()) {
    throw new IllegalStateException("EnumMap key type not an enum: " + type.getKeyType());
}
mapper.readValue(json, type);

Type guard

boolean hasEnumKey(JavaType t) {
    return t.isMapLikeType() && t.getKeyType().isEnumImplType();
}

Prevention

When it happens

Trigger: Deserializing into a raw EnumMap, an EnumMap field whose generic key type is erased/bound to a non-enum type, or a Map<K,V> where K is an enum type variable that cannot be reified. Also when type erasure or a TypeFactory miscalculation leaves the key type as a placeholder.

Common situations: Using a raw EnumMap field/parameter without generics; deserializing into a generic type whose enum key is supplied via a TypeVariable that never gets bound; subclassing EnumMap without preserving generics; converting from Object.class or Map.class into EnumMap.

Related errors


AI-assisted analysis of FasterXML/jackson-databind@87876ca5c0 (2026-08-11). Data as JSON: /api/errors/610f77d9f2b43c86. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/tools/jackson/databind/deser/BasicDeserializerFactory.java:925

            Class<?> mapClass = type.getRawClass();
            // [databind#1853]: Map `Map<ENUM,x>` to `EnumMap<ENUM,x>`
            if ((mapClass == Map.class) && keyType.isEnumType()) {
                mapClass = EnumMap.class;
                type = (MapType) config.getTypeFactory().constructSpecializedType(type, mapClass);
//                type = (MapType) config.getTypeFactory().constructMapType(mapClass, keyType, contentType);
            }
            if (EnumMap.class.isAssignableFrom(mapClass)) {
                ValueInstantiator inst;

                // 06-Mar-2017, tatu: Should only need to check ValueInstantiator for
                //    custom sub-classes, see [databind#1544]
                if (mapClass == EnumMap.class) {
                    inst = null;
                } else {
                    inst = findValueInstantiator(ctxt, beanDescRef);
                }
                if (!keyType.isEnumImplType()) {
                    throw new IllegalArgumentException("Cannot construct EnumMap; generic (key) type not available");
                }
                deser = new EnumMapDeserializer(type, inst, null,
                        contentDeser, contentTypeDeser, null);
            }

            // Otherwise, generic handler works ok.

            /* But there is one more twist: if we are being asked to instantiate
             * an interface or abstract Map, we need to either find something
             * that implements the thing, or give up.
             *
             * Note that we do NOT try to guess based on secondary interfaces
             * here; that would probably not work correctly since casts would
             * fail later on (as the primary type is not the interface we'd
             * be implementing)
             */
            if (deser == null) {
                if (type.isInterface() || type.isAbstract()) {

View on GitHub (pinned to 87876ca5c0)