hibernate/hibernate-orm · error · MappingException

map index element must specify a type: %s

Error message

map index element must specify a type: %s

What it means

For a <map> with a basic (non-entity, non-composite) index, Hibernate builds a BasicValue for the map key from <map-key>. There is no backing Java property to reflect on, so if after binding the type information value.isTypeSpecified() is false - no type= on <map-key> and nothing inferable - the key column's type is unknown and binding fails.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java:3699

				componentBinding,
				null,
				pluralAttributeSource.getName(),
				false
		);
		collectionBinding.setIndex( componentBinding );
	}

	private void bindBasicMapKey(
			MappingDocument mappingDocument,
			IndexedPluralAttributeSource pluralAttributeSource,
			org.hibernate.mapping.Map collectionBinding,
			PluralAttributeMapKeySourceBasic mapKeySource) {
		final var value =
				new BasicValue( mappingDocument,
						collectionBinding.getCollectionTable() );
		bindSimpleValueType( mappingDocument, mapKeySource.getTypeInformation(), value );
		if ( !value.isTypeSpecified() ) {
			throw new MappingException(
					"map index element must specify a type: "
					+ pluralAttributeSource.getAttributeRole().getFullPath(),
					mappingDocument.getOrigin()
			);
		}

		relationalObjectBinder.bindColumnsAndFormulas(
				mappingDocument,
				mapKeySource.getRelationalValueSources(),
				value,
				true,
				context -> database.toIdentifier( IndexedCollection.DEFAULT_INDEX_COLUMN_NAME )
		);

		collectionBinding.setIndex( value );
	}

	private class ManyToOneColumnBinder implements ImplicitColumnNamingSecondPass {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add the needed type to the <map-key> element, e.g. <map-key column='lang' type='string'/>
  2. For composite keys use <composite-map-key class='...'> and for entity keys use <map-key-many-to-many class='...'> instead of a typed basic <map-key>

Example fix

// before
<map name='labels' table='labels'>
    <key column='item_id'/>
    <map-key column='lang'/>
    <element column='text' type='string'/>
</map>

// after
<map name='labels' table='labels'>
    <key column='item_id'/>
    <map-key column='lang' type='string'/>
    <element column='text' type='string'/>
</map>
Defensive patterns

Strategy: validation

Validate before calling

NodeList maps = doc.getElementsByTagName("map");
for (int i = 0; i < maps.getLength(); i++) {
    Element map = (Element) maps.item(i);
    NodeList keys = map.getElementsByTagName("map-key");
    for (int j = 0; j < keys.getLength(); j++) {
        Element k = (Element) keys.item(j);
        if (k.getAttributeNode("type") == null || k.getAttribute("type").isBlank()) {
            throw new IllegalStateException("<map-key> of " + map.getAttribute("name") + " must declare a type");
        }
    }
}

Try / catch

catch (MappingException e) at bootstrap; the message names the map's attribute role. Add the matching type attribute to that <map-key> and rebuild.

Prevention

When it happens

Trigger: <map><key .../><map-key column='lang'/><element .../></map> - a <map-key> element without a type attribute; also a map-key whose type attribute name is unresolvable so isTypeSpecified() ends up false.

Common situations: Converting a <set> to a <map> and forgetting the key type; assuming Hibernate guesses the index type like it can for POJO <id> properties (it cannot - a map key has no property to reflect on).

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/81d5d4f9c0462f7c. Report an issue: GitHub.