{"record":{"id":"208b2abd6877c110","repo":"hibernate/hibernate-orm","slug":"constraint-paths-were-already-set-s","errorCode":null,"errorMessage":"Constraint paths were already set: %s","messagePattern":"Constraint paths were already set: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/insert/SqmConflictClause.java","lineNumber":78,"sourceCode":"\t\tthis.updateAction = updateAction;\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic SqmRoot<T> getExcludedRoot() {\n\t\treturn excludedRoot;\n\t}\n\n\t@Override\n\tpublic @Nullable String getConstraintName() {\n\t\treturn constraintName;\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic SqmConflictClause<T> conflictOnConstraint(@Nullable String constraintName) {\n\t\tif ( constraintPaths != null && !constraintPaths.isEmpty() ) {\n\t\t\tthrow new IllegalStateException( \"Constraint paths were already set: \" + constraintPaths );\n\t\t}\n\t\tthis.constraintName = constraintName;\n\t\treturn this;\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic JpaConflictClause<T> conflictOnConstraintAttributes(String... attributes) {\n\t\tfinal ArrayList<SqmPath<?>> paths = new ArrayList<>( attributes.length );\n\t\tfor ( String attribute : attributes ) {\n\t\t\tpaths.add( insertStatement.getTarget().get( attribute ) );\n\t\t}\n\t\treturn conflictOnConstraintPaths( paths );\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic JpaConflictClause<T> conflictOnConstraintAttributes(@Nonnull SingularAttribute<T, ?>... attributes) {","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/insert/SqmConflictClause.java#L60-L96","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Delete one of the two calls — keep either conflictOnConstraint(name) or conflictOnConstraintAttributes/Paths(columns), not both","If you must switch dynamically, build the conflict clause once based on configuration instead of mutating the same clause object","Before setting the name, guard with `clause.getConstraintPaths().isEmpty()`","Check the final statement shape once in a unit test so regressions surface at build time"],"exampleFix":"// before\ninsert.onConflict()\n     .conflictOnConstraintAttributes(\"ssn\")\n     .conflictOnConstraint(\"uk_person_ssn\"); // IllegalStateException\n\n// after\ninsert.onConflict()\n     .conflictOnConstraintAttributes(\"ssn\");","handlingStrategy":"validation","validationCode":"if (clause.getConstraintPaths() == null || clause.getConstraintPaths().isEmpty()) {\n    clause.conflictOnConstraint( constraintName );\n} else {\n    throw new IllegalStateException(\"Conflict target already declared by paths\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Model the conflict target as one choice (name XOR columns) in your builder config","Never chain both conflictOnConstraint* families on the same clause","Cover upsert statements in unit tests that render the SQL"],"tags":["hibernate","criteria-api","upsert","on-conflict","builder-state"],"backgroundTag":"upsert-conflict-clause-misuse","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}