hibernate/hibernate-orm · error · IllegalArgumentException

Couldn't infer conflict constraint columns

Error message

Couldn't infer conflict constraint columns

What it means

For HQL `insert ... on conflict` without explicit constraint columns, the CTE emulation falls back to the entity's primary key columns to build the row-match predicate. buildColumnMatchPredicate returns null when those key columns aren't present among the insert target columns (typical when the id is generated and omitted), and the handler aborts with this IllegalArgumentException because no conflict predicate could be inferred.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java:1100

					null,
					sessionFactory
			);
			subquery.getSelectClause().addSqlSelection(
					new SqlSelectionImpl( new QueryLiteral<>( 1, nodeBuilder.getIntegerType() ) )
			);
			subquery.getFromClause().addRoot( tableGroup );
			List<String> columnsToMatch;
			if ( constraintColumnNames.isEmpty() ) {
				// Assume the primary key columns
				Predicate predicate = buildColumnMatchPredicate(
						columnsToMatch = Arrays.asList( ((EntityPersister) entityDescriptor).getKeyColumns( tableIndex ) ),
						insertStatement,
						false,
						true,
						booleanType
				);
				if ( predicate == null ) {
					throw new IllegalArgumentException( "Couldn't infer conflict constraint columns" );
				}
				subquery.applyPredicate( predicate );
			}
			else {
				columnsToMatch = constraintColumnNames;
				subquery.applyPredicate(
						buildColumnMatchPredicate( constraintColumnNames, insertStatement, true, true, booleanType ) );
			}

			insertQuerySpec.applyPredicate( new ExistsPredicate( subquery, true, booleanType ) );

			// Emulate the conflict do update clause by creating a separate update CTEs
			if ( conflictClause.isDoUpdate() ) {
				final TableGroup temporaryTableGroup = insertQuerySpec.getFromClause().getRoots().get( 0 );
				final QuerySpec renamingSubquery = new QuerySpec( false, 1 );
				final List<String> columnNames = buildCteRenaming(
						renamingSubquery,
						temporaryTableGroup,

View on GitHub (pinned to fad1729dce)

Solutions

  1. Name the conflict columns explicitly and make sure they are inserted: `on conflict (email)`
  2. Include the id/PK in the insert target list when you want PK-based conflict detection (e.g. insert...select that supplies ids)
  3. Use native SQL upsert (INSERT ... ON CONFLICT / MERGE) for dialect-specific upserts on non-native databases

Example fix

// before
insert into Customer(email, name)
select o.email, o.name from Lead o
on conflict

// after
insert into Customer(email, name)
select o.email, o.name from Lead o
on conflict (email)
Defensive patterns

Strategy: validation

Validate before calling

// Always name conflict columns explicitly when ids are generated
if (idIsGenerated(entityClass) && conflictClauseColumns.isEmpty()) {
    throw new IllegalArgumentException(
        "on conflict needs explicit columns when the PK is not inserted");
}

Try / catch

try {
    em.createQuery(insertHql).executeUpdate();
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("infer conflict constraint")) {
        throw new QueryBuildException(
            "Add explicit conflict columns, e.g. 'on conflict (email)'", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: `insert into Customer(email, name) select ... on conflict` (no column list) with a @GeneratedValue id: the PK is not among targetColumns, so PK inference finds nothing to match; happens on the emulation path (dialects without native upsert, or when the CTE handler emulates the clause).

Common situations: Portable upserts written as HQL insert...select...on-conflict with generated ids — they work on PostgreSQL (native ON CONFLICT) but fail when the same query runs through emulation on other databases; adopting Hibernate 6.5+ conflict clauses without specifying an index.

Related errors


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