hibernate/hibernate-orm · error · TransientPropertyValueException

Instance of '%s' references an unsaved transient instance of

Error message

Instance of '%s' references an unsaved transient instance of '%s' (persist the transient instance before flushing)

What it means

The graph-based flush planner tracks inserts whose non-nullable FK targets are still transient (Decomposer.trackUnresolvedInsert). If the dependencies are never satisfied by the end of planning, Decomposer throws TransientPropertyValueException ("Instance of 'A' references an unsaved transient instance of 'B' (persist the transient instance before flushing)", Decomposer.java:478). It is the graph-queue equivalent of the classic unresolved-insert failure: a mandatory association points at an unsaved entity and nothing cascades or persists it.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/action/queue/internal/decompose/Decomposer.java:478

			return;
		}

		// Get first unresolved insert for error reporting
		final var firstEntry = unresolvedInserts.entrySet().iterator().next();
		final AbstractEntityInsertAction firstInsert = firstEntry.getKey();
		final NonNullableTransientDependencies dependencies = firstEntry.getValue();

		// Get first transient dependency for error message
		final Object firstTransient = dependencies.getNonNullableTransientEntities().iterator().next();
		final String firstPropertyPath = dependencies.getNonNullableTransientPropertyPaths(firstTransient).iterator().next();

		final String entityName = firstInsert.getEntityName();
		final String transientEntityName = session.guessEntityName(firstTransient);

		// Log all unresolved dependencies for debugging
		logUnresolvedDependencies();

		throw new TransientPropertyValueException(
				"Instance of '" + entityName +
						"' references an unsaved transient instance of '" + transientEntityName +
						"' (persist the transient instance before flushing)",
				transientEntityName,
				entityName,
				firstPropertyPath
		);
	}

	/// Log details about all unresolved dependencies for debugging purposes.
	private void logUnresolvedDependencies() {
		// Log via ActionLogger or similar when available
		// For now, just track the data for error reporting
		// The error message already includes the first unresolved dependency
	}

	/// Clear all tracked unresolved inserts. Used for cleanup.
	public void clear() {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add CascadeType.PERSIST (typically {PERSIST, MERGE}) to the association that references the transient instance
  2. Or persist the referenced entity explicitly before flush: session.persist(child) then flush
  3. If the reference is optional at flush time, make the column/property nullable and populate it in a later step
  4. If dependencies should be resolvable and are not, verify cascade reaches the instance through the exact path you traverse (cascades only follow mapped associations)

Example fix

// before - required tag never persisted; graph flush throws TransientPropertyValueException
Article a = new Article();
a.setTag(new Tag("java"));            // @ManyToOne(optional=false), no cascade
em.persist(a);
em.flush();

// after - cascade the persist through the association
@ManyToOne(optional = false, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Tag tag;
Defensive patterns

Strategy: validation

Validate before calling

// validate required references before flushing the graph
if (article.getTag() != null && !em.contains(article.getTag())) {
    em.persist(article.getTag()); // satisfies the non-null FK dependency
}
em.persist(article);
em.flush();

Type guard

static boolean isTransient(Object entity, EntityManager em) {
    return entity != null && !em.contains(entity);
}

Try / catch

try {
    em.flush();
} catch (TransientPropertyValueException e) {
    // property path tells you exactly which association was unsaved
    // fix cascade/persist, then rerun the use case in a fresh transaction
}

Prevention

When it happens

Trigger: Setting a non-nullable @ManyToOne to a brand-new entity without CascadeType.PERSIST and flushing under the graph queue (default in 8.x); IDENTITY inserts deferred via hibernate.flush.queue.graph.defer_identity_inserts=true whose transient FK dependencies are never resolved; object graphs assembled in the wrong direction (only the many-side persisted).

Common situations: Same as classic TransientPropertyValueException: missing cascade after mapping refactor; builders/tests persisting only the aggregate root while a required child reference is new; switching an association from nullable to mandatory without adding cascade; enabling deferred identity inserts on 8.x exposing previously hidden ordering gaps.

Related errors


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