hibernate/hibernate-orm · error · IllegalArgumentException

null key for collection: {}

Error message

null key for collection: {}

What it means

BasicCollectionPersister.applyInsertRowValues decomposes the collection's FK key value into JDBC bindings when writing a collection row; a null key is illegal because every row must reference its owner, so it throws IllegalArgumentException('null key for collection: ' + fullRole). It surfaces at flush time during collection recreate/insert.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java:365

		final var pluralAttribute = getAttributeMapping();
		assert pluralAttribute != null;
		final var foreignKeyDescriptor = pluralAttribute.getKeyDescriptor();
		assert foreignKeyDescriptor != null;
		final var insertBuilder = new TableInsertBuilderStandard( this, tableReference, getFactory() );
		applyInsertDetails( insertBuilder );
		//noinspection unchecked,rawtypes
		return (TableMutation) insertBuilder.buildMutation();
	}

	private void applyInsertRowValues(
			PersistentCollection<?> collection,
			Object key,
			Object rowValue,
			int rowPosition,
			SharedSessionContractImplementor session,
			JdbcValueBindings jdbcValueBindings) {
		if ( key == null ) {
			throw new IllegalArgumentException( "null key for collection: " + getNavigableRole().getFullPath() );
		}
		final var attributeMapping = getAttributeMapping();
		attributeMapping.getKeyDescriptor().getKeyPart().decompose(
				key,
				0,
				jdbcValueBindings,
				null,
				DEFAULT_VALUE_SETTER,
				session
		);

		final var identifierDescriptor = attributeMapping.getIdentifierDescriptor();
		if ( identifierDescriptor != null ) {
			identifierDescriptor.decompose(
					collection.getIdentifier( rowValue, rowPosition ),
					0,
					jdbcValueBindings,
					null,

View on GitHub (pinned to fad1729dce)

Solutions

  1. Persist the owning entity before or with the collection: enable cascade (CascadeType.PERSIST/ALL) or save the owner first so its key is non-null at flush
  2. Fix the collection key mapping: map the FK to the owner's id column(s) / non-null property, and make the join column non-nullable if the model guarantees it
  3. Never reassign a PersistentCollection instance from one owner to another — create a new collection on the new owner and save the owner first
  4. In tests/fixtures, set the owner's generated/assigned id before adding elements and flushing

Example fix

// before
Order order = new Order(); // transient, no id, no cascade
List<OrderLine> lines = otherOrder.getLines(); // detached collection
order.setLines(lines);
em.persist(order); // em.persist(order) only -> flush writes collection rows with null key

// after
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderLine> lines = new ArrayList<>();
// copy elements, own them, cascade persists owner before rows
Order order = new Order();
otherOrder.getLines().forEach(l -> order.addLine(l));
em.persist(order);
Defensive patterns

Strategy: try-catch

Validate before calling

// Before flush: the owner must be persisted so the collection FK resolves non-null
if (owner.getId() == null) {
  session.persist(owner); // or ensure cascade = PERSIST/ALL on the collection
}
// avoid reusing detached PersistentCollection instances across owners
if (lines instanceof org.hibernate.collection.spi.PersistentCollection<?> pc
    && pc.getOwner() != null && pc.getOwner() != owner) {
  owner.setLines(new ArrayList<>(lines)); // fresh collection owned by 'owner'
}

Try / catch

try {
  session.persist(owner);
  session.flush();
} catch (IllegalArgumentException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("null key for collection")) {
    // collection FK was null at row-insert time: save/attach the owner (cascade), fix join-column mapping, then retry
    log.warn("Null collection key for {} — persisting owner and retrying", e.getMessage());
    throw e; // after fixing owner state, a new flush attempt is safe
  }
  throw e;
}

Prevention

When it happens

Trigger: Flushing a collection whose owner key resolves to null: the owning instance is transient and not yet persisted (no cascade, no id), the FK is mapped from a property that is null at flush (nullable join column / property-ref style key), or a detached collection instance was attached to a new, unsaved owner.

Common situations: Missing CascadeType.PERSIST/ALL so the owner never gets saved before the collection row write; manually assigning a collection from one entity to a fresh entity without saving it; join-column mappings referencing a nullable or unset property; test fixtures constructing object graphs that skip the owner's id.

Related errors


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