hibernate/hibernate-orm · error · IllegalArgumentException

Couldn't find conflict constraint column [${constraintColumn

Error message

Couldn't find conflict constraint column [${constraintColumnName}] in insert target columns: ${targetColumnNames}

What it means

When an explicit ON CONFLICT column cannot be found among the insert's target columns, buildColumnMatchPredicate throws this IllegalArgumentException (errorIfMissing=true path). The emulation needs each named conflict column to also be an inserted column so it can build the match predicate — unlike native SQL where the conflict index column need not be inserted.

Source

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

												"excluded",
												columnReference.getColumnExpression(),
												false,
												null,
												columnReference.getJdbcMapping()
										),
										booleanType
								)
						);
					}
					continue OUTER;
				}
			}
			if ( errorIfMissing ) {
				// Should never happen
				final List<String> targetColumnNames = targetColumns.stream()
						.map( ColumnReference::getColumnExpression )
						.collect( Collectors.toList() );
				throw new IllegalArgumentException( "Couldn't find conflict constraint column [" + constraintColumnName + "] in insert target columns: " + targetColumnNames );
			}
			return null;
		}
		return predicate;
	}

	private List<Assignment> getCompatibleAssignments(InsertSelectStatement dmlStatement, ConflictClause conflictClause) {
		if ( conflictClause.isDoNothing() ) {
			return Collections.emptyList();
		}
		List<Assignment> compatibleAssignments = null;
		final List<Assignment> assignments = conflictClause.getAssignments();
		for ( Assignment assignment : assignments ) {
			for ( ColumnReference targetColumn : dmlStatement.getTargetColumns() ) {
				if ( assignment.getAssignable().getColumnReferences().contains( targetColumn ) ) {
					if ( compatibleAssignments == null ) {
						compatibleAssignments = new ArrayList<>( assignments.size() );
					}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add the conflict column to the insert target list: `insert into Customer(id, email, name) ... on conflict (email)`
  2. Choose a conflict column that IS among the inserted columns
  3. Validate at query-build time that every conflict column is in the target list

Example fix

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

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

Strategy: validation

Validate before calling

// Validate: every explicit conflict column must be an insert target column
Set<String> targets = new HashSet<>(targetColumns);
for (String c : conflictColumns) {
    if (!targets.contains(c)) {
        throw new IllegalArgumentException(
            "Conflict column " + c + " must also appear in the insert target list");
    }
}

Try / catch

try {
    em.createQuery(insertHql).executeUpdate();
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Couldn't find conflict constraint column")) {
        throw new QueryBuildException("Add the conflict column to the insert target list", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: `insert into Customer(id, name) select ... on conflict (email)` — email is named as the conflict column but is absent from the target column list; renaming attributes so the conflict clause references a property no longer inserted.

Common situations: Writing HQL `on conflict (col)` with PostgreSQL-native semantics in mind (where the referenced index column doesn't have to appear in the INSERT list); partial insert column lists combined with unique-key conflict handling.

Related errors


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