hibernate/hibernate-orm · error · IllegalStateException

Constraint paths were already set: %s

Error message

Constraint paths were already set: %s

What it means

SqmConflictClause.conflictOnConstraint(String) throws IllegalStateException when constraintPaths are already populated. Hibernate's INSERT ... ON CONFLICT support (JPA 3.2 style, exposed via HibernateCriteriaBuilder) lets you designate the conflict target either by constraint NAME or by constraint PATHS (the index columns) — never both. The object tracks which form was chosen, and switching from paths to name after paths were set is an illegal state transition on the builder.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/insert/SqmConflictClause.java:78

		this.updateAction = updateAction;
	}

	@Nonnull
	@Override
	public SqmRoot<T> getExcludedRoot() {
		return excludedRoot;
	}

	@Override
	public @Nullable String getConstraintName() {
		return constraintName;
	}

	@Nonnull
	@Override
	public SqmConflictClause<T> conflictOnConstraint(@Nullable String constraintName) {
		if ( constraintPaths != null && !constraintPaths.isEmpty() ) {
			throw new IllegalStateException( "Constraint paths were already set: " + constraintPaths );
		}
		this.constraintName = constraintName;
		return this;
	}

	@Nonnull
	@Override
	public JpaConflictClause<T> conflictOnConstraintAttributes(String... attributes) {
		final ArrayList<SqmPath<?>> paths = new ArrayList<>( attributes.length );
		for ( String attribute : attributes ) {
			paths.add( insertStatement.getTarget().get( attribute ) );
		}
		return conflictOnConstraintPaths( paths );
	}

	@Nonnull
	@Override
	public JpaConflictClause<T> conflictOnConstraintAttributes(@Nonnull SingularAttribute<T, ?>... attributes) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Delete one of the two calls — keep either conflictOnConstraint(name) or conflictOnConstraintAttributes/Paths(columns), not both
  2. If you must switch dynamically, build the conflict clause once based on configuration instead of mutating the same clause object
  3. Before setting the name, guard with `clause.getConstraintPaths().isEmpty()`
  4. Check the final statement shape once in a unit test so regressions surface at build time

Example fix

// before
insert.onConflict()
     .conflictOnConstraintAttributes("ssn")
     .conflictOnConstraint("uk_person_ssn"); // IllegalStateException

// after
insert.onConflict()
     .conflictOnConstraintAttributes("ssn");
Defensive patterns

Strategy: validation

Validate before calling

if (clause.getConstraintPaths() == null || clause.getConstraintPaths().isEmpty()) {
    clause.conflictOnConstraint( constraintName );
} else {
    throw new IllegalStateException("Conflict target already declared by paths");
}

Prevention

When it happens

Trigger: Chaining `insert.onConflict().conflictOnConstraintAttributes("ssn").conflictOnConstraint("uk_person_ssn")` — first setting paths via conflictOnConstraintAttributes/Paths, then also setting a name; fluent-builder code that 'fills in' both fields unconditionally; copying both variants together while merging examples from docs.

Common situations: Migrating between the two styles during development (columns-based for dialects where the index name is unknown, name-based where it is managed by DBA) and leaving both calls in; generic query builders that set every configured option; misunderstanding that ON CONFLICT needs exactly one arbiter specification (name OR columns) in PostgreSQL-family SQL.

Related errors


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