hibernate/hibernate-orm · error · IllegalStateException

Retrieved key was null, but to-one is not nullable : %s

Error message

Retrieved key was null, but to-one is not nullable : %s

What it means

While applying follow-on table-lock results, TableLock.ToOneResultHandler reads the FK key value for a to-one association. If the database returned null but the mapping declares the association non-nullable (@ManyToOne(optional=false), @JoinColumn(nullable=false), or a mandatory FK), it throws IllegalStateException with the attribute's full navigable path. The mapping and the stored data disagree.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/sql/exec/internal/lock/TableLock.java:314

			applyModelState( entityDetails, statePosition, stateValue );
		}
	}

	// Used by Hibernate Reactive
	protected static class ToOneResultHandler extends AbstractResultHandler {
		protected final ToOneAttributeMapping toOne;

		public ToOneResultHandler(Integer statePosition, ToOneAttributeMapping toOne) {
			super( statePosition );
			this.toOne = toOne;
		}

		@Override
		public void applyResult(Object stateValue, EntityDetails entityDetails, SharedSessionContractImplementor session) {
			final Object reference;
			if ( stateValue == null ) {
				if ( !toOne.isNullable() ) {
					throw new IllegalStateException( "Retrieved key was null, but to-one is not nullable : " + toOne.getNavigableRole().getFullPath() );
				}
				else {
					reference = null;
				}
			}
			else {
				reference = session.internalLoad(
						toOne.getAssociatedEntityMappingType().getEntityName(),
						stateValue,
						false,
						toOne.isNullable()
				);
			}
			applyLoadedState( entityDetails, statePosition, reference );
			applyModelState( entityDetails, statePosition, reference );
		}
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Fix the data: UPDATE the offending rows so the FK is populated (find them with 'where <fk_column> is null' on the mapped table)
  2. Add/restore the NOT NULL constraint so violations cannot re-enter
  3. If nulls are legitimate, correct the mapping: @ManyToOne(optional=true) / @JoinColumn(nullable=true)
  4. During migration, exclude broken rows from the locking query until data is repaired

Example fix

// before
@ManyToOne( optional = false )
@JoinColumn( name = "customer_id", nullable = false )
private Customer customer; // DB rows contain null customer_id

// after (if nulls are legitimate)
@ManyToOne( optional = true )
@JoinColumn( name = "customer_id" )
private Customer customer;
-- or fix data: UPDATE Orders SET customer_id = :fallback WHERE customer_id IS NULL;
Defensive patterns

Strategy: validation

Validate before calling

-- verify mapping matches data before enabling non-optional to-ones
SELECT count(*) FROM child_table WHERE fk_column IS NULL;
-- must return 0 for @ManyToOne(optional=false) mappings

Prevention

When it happens

Trigger: A row has NULL in the FK column of a to-one mapped as optional=false, read while applying results of a pessimistic/follow-on table lock (session.lock, lock-mode queries on collections or associations).

Common situations: Legacy or manually imported data violating NOT NULL; the DB constraint missing or dropped so nulls entered; the mapping tightened (optional=true to false) after null data already existed; hbm2ddl/validation disabled so schema and mapping drifted.

Related errors


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