{"record":{"id":"2954b95f1ac4de16","repo":"hibernate/hibernate-orm","slug":"attribute-was-not-a-map-collectionmembertype","errorCode":null,"errorMessage":"Attribute was not a Map : ${collectionMemberType}","messagePattern":"Attribute was not a Map : (.+?)","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AutoApplicableConverterDescriptorStandardImpl.java","lineNumber":110,"sourceCode":"\t}\n\n\t@Override\n\tpublic ConverterDescriptor<?,?> getAutoAppliedConverterDescriptorForMapKey(\n\t\t\tMemberDetails memberDetails,\n\t\t\tMetadataBuildingContext context) {\n\n\t\tfinal var collectionMemberType = actualMemberType( memberDetails );\n\t\tfinal Type keyType;\n\t\tif ( Map.class.isAssignableFrom( erasedType( collectionMemberType ) ) ) {\n\t\t\tfinal var typeArguments =\n\t\t\t\t\ttypeArguments( Map.class, collectionMemberType );\n\t\t\tif ( typeArguments.length == 0 ) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tkeyType = typeArguments[0];\n\t\t}\n\t\telse {\n\t\t\tthrow new HibernateException( \"Attribute was not a Map : \" + collectionMemberType );\n\t\t}\n\n\t\treturn isAssignableFrom( linkedConverterDescriptor.getDomainValueResolvedType(),\n\t\t\t\t\t\tcanonicalizePrimitive( keyType ) )\n\t\t\t\t? linkedConverterDescriptor\n\t\t\t\t: null;\n\t}\n\n\n}\n","sourceCodeStart":92,"sourceCodeEnd":121,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/AutoApplicableConverterDescriptorStandardImpl.java#L92-L121","documentation":"Hibernate throws this HibernateException while binding metadata, inside AutoApplicableConverterDescriptorStandardImpl.getAutoAppliedConverterDescriptorForMapKey. When a converter with autoApply=true is considered for the KEY of a map-style plural attribute, Hibernate resolves the attribute's actual member type and requires it to be assignable to java.util.Map; if the member resolves to some other type (List, Set, array, a raw type, or a custom collection), the internal invariant is broken and binding fails.","triggerScenarios":"A @Converter(autoApply=true) converter is registered (via @Converter, MetadataBuilder.addAttributeConverter, or autoApplyDetection) and Hibernate resolves map-key auto-apply for a plural attribute whose declared/actual type is not java.util.Map, e.g. a List/Set field processed through map-key converter resolution, or a custom collection implementation masking the Map interface.","commonSituations":"Upgrading Hibernate between 6.x and 7.x where auto-apply resolution for collection keys/elements was reworked; introducing an autoApply converter into a codebase that already uses custom collection types; generics erased by proxying or raw types.","solutions":["Declare the plural attribute as Map<KeyType, ValueType> if the map-key converter is intended to apply","If the attribute is intentionally a List/Set/array, stop the converter from auto-applying to it with @Convert(disableConversion = true) on the attribute or @Converter(autoApply = false)","Narrow the converter's domain type so it no longer matches the attribute's key type, or register it explicitly with @Convert instead of autoApply","If the member is a custom collection type, make sure its class ultimately implements java.util.Map when used where a map key is expected","Report to Hibernate (HHH) if the attribute is a plain Map and it still fails - a resolution bug in auto-apply handling"],"exampleFix":"// before: auto-apply converter + non-Map plural attribute\n@Converter(autoApply = true)\npublic class StatusConverter implements AttributeConverter<Status, String> { ... }\n\n@Entity\nclass Order {\n    @ElementCollection\n    private List<Status> statuses = new ArrayList<>();  // triggers map-key resolution internally\n}\n\n// after: either use a Map so map-key auto-apply is well-defined\n@ElementCollection\nprivate Map<Status, Integer> statusCounts = new HashMap<>();\n\n// ...or exclude the attribute from auto-apply\n@Convert(disableConversion = true)\n@ElementCollection\nprivate List<Status> statuses = new ArrayList<>();","handlingStrategy":"type-guard","validationCode":"// Before boot: every attribute that map-key converter resolution will visit must be a Map\nstatic boolean allPluralMapAttributesAreMaps(Class<?> entity) {\n    for (Field f : entity.getDeclaredFields()) {\n        if (f.isAnnotationPresent(ElementCollection.class)\n                || f.isAnnotationPresent(OneToMany.class)\n                || f.isAnnotationPresent(ManyToMany.class)) {\n            if (f.isAnnotationPresent(MapKey.class)\n                    || f.isAnnotationPresent(MapKeyClass.class)\n                    || f.isAnnotationPresent(MapKeyEnumerated.class)) {\n                if (!Map.class.isAssignableFrom(f.getType())) return false;\n            }\n        }\n    }\n    return true;\n}","typeGuard":"// Type guard usable on a single suspected field\nstatic boolean isMapAttribute(Field f) {\n    return Map.class.isAssignableFrom(f.getType());\n}","tryCatchPattern":"try {\n    sessionFactory = new MetadataSources(registry).addAnnotatedClass(Order.class)\n            .buildMetadata().buildSessionFactory();\n} catch (HibernateException e) {  // \"Attribute was not a Map\" surfaces here\n    throw new IllegalStateException(\"Bad plural-attribute shape for auto-apply converter: \" + e.getMessage(), e);\n}","preventionTips":["Prefer Map<Key,Value> declarations whenever a converter must apply to keys","Register converters explicitly with @Convert(converter = ...) instead of autoApply when only a few attributes need them","Keep an integration test that builds the SessionFactory so mapping errors surface in CI, not production"],"tags":["hibernate","jpa","converter","auto-apply","mapping","bootstrap"],"backgroundTag":"jpa-converter-auto-apply-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}