hibernate/hibernate-orm · error · IllegalStateException
Unbreakable cycle detected for SCC: %s
Error message
Unbreakable cycle detected for SCC: %s
What it means
While making the flush dependency graph schedulable, CycleBreaker found a cycle with no edge it is allowed to break: no preferred-order edge, no update edge, and hasAnyBreakableEdge() reports nothing breakable at all. Every dependency in the cycle is a required (non-nullable, non-deferrable) FK/unique constraint, so any execution order would violate at least one constraint at flush time. Hibernate refuses to schedule doomed SQL and throws this IllegalStateException instead.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/action/queue/internal/plan/CycleBreaker.java:132
}
continue;
}
final GraphEdge chosen = chooseEdgeToBreak(cycle);
if (chosen == null) {
if (isDeleteOnlyCycle(cycle)) {
breakArbitraryDeleteEdge(cycle, deferrableConstraintMode);
continue;
}
final GraphEdge updateEdge = findUpdateEdge(cycle);
if (updateEdge != null) {
updateEdge.setBroken(true);
continue;
}
if (!hasAnyBreakableEdge(cycle)) {
throw new IllegalStateException("Unbreakable cycle detected for SCC: " + describeScc(scc));
}
final GraphEdge deferrableEdge = findEffectivelyDeferredEdge(cycle, deferrableConstraintMode);
if (deferrableEdge != null) {
deferrableEdge.setBroken(true);
continue;
}
// Break a non-DELETE edge as last resort.
final GraphEdge nonDeleteEdge = findNonDeleteEdge(cycle);
if (nonDeleteEdge != null) {
nonDeleteEdge.setBroken(true);
// Only explicit patchable edges actually install a patch; required
// edges return null from GraphEdge#getPatchCycleType().
if (nonDeleteEdge.getPatchNode() != null &&
nonDeleteEdge.getPatchNode().group().kind() == MutationKind.INSERT) {
installPatchForEdge(nonDeleteEdge);
}View on GitHub (pinned to fad1729dce)
Solutions
- Make at least one association in the cycle nullable (remove optional=false / nullable=false) so the planner can apply NULL-then-patch.
- Restructure the model to break the cycle: make one direction unidirectional or move the FK to a nullable join table.
- Assign in phases: persist A with the reference null, flush, then set B.a / A.b and flush again.
- On PostgreSQL/Oracle, declare the FKs DEFERRABLE INITIALLY DEFERRED and enable Hibernate's deferrable constraint mode so edges in the cycle can be broken.
Example fix
// before - mutual NOT NULL FKs, unbreakable cycle
@Entity class A { @ManyToOne(optional = false) B b; }
@Entity class B { @ManyToOne(optional = false) A a; }
// after - one side may be temporarily null
@Entity class A { @ManyToOne(optional = false) B b; }
@Entity class B { @ManyToOne A a; } Defensive patterns
Strategy: validation
Validate before calling
// phase inserts so the NOT NULL cycle never exists within one flush A a = new A(); B b = new B(); a.setB(b); // B.a stays null for now session.persist(a); session.persist(b); session.flush(); b.setA(a); // close the cycle after both rows exist
Try / catch
try {
session.flush();
}
catch (HibernateException e) {
if (e.getMessage() != null && e.getMessage().contains("Unbreakable cycle detected for SCC")) {
tx.rollback(); // reapply changes in phases with intermediate flushes
}
else throw e;
} Prevention
- Never map both directions of a bidirectional association as optional=false
- Use DEFERRABLE INITIALLY DEFERRED constraints for genuinely cyclic schemas
- Add integration tests that mutate cyclic object graphs inside one transaction
When it happens
Trigger: Inserting or updating entities that reference each other through NOT NULL foreign keys in one transaction, e.g. @ManyToOne(optional=false) on both directions of A<->B, or a self-referential entity with a mandatory parent; schemas where FKs previously were deferrable but no longer are.
Common situations: Bidirectional @ManyToOne/@OneToOne marked optional=false on both sides; schema export produced NOT NULL FK columns on both tables; migrating from the legacy action queue whose ordering worked by accident; MySQL/MariaDB which lack deferred FK checks.
Related errors
- Unbreakable unique update cycle detected for SCC: %s
- Unsupported JdbcOperation type: %s
- Composite key breakdown not yet implemented
- There are delayed insert actions before operation as cascade
- null key for collection: {}
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/b647a8ecc3adf1be.
Report an issue: GitHub.