hs-web/hsweb-framework · error · ValidationException
error.tree_entity_cyclic_dependency
error.tree_entity_cyclic_dependency
Error message
error.tree_entity_cyclic_dependency
What it means
TreeSortServiceHelper validates tree-structured entities for cyclic parent/child references. During DFS (checkCyclicDependency), if a node ID is encountered a second time in the current path (container.add returns false), a ValidationException on the `parentId` field with code `error.tree_entity_cyclic_dependency` is thrown.
Solutions
- Fix the offending entities so no parentId chain loops (each node's ancestor chain ends at a root).
- In the UI, forbid dropping a node into itself or its descendants.
- Add a pre-save integrity check that rejects cycles and reports the involved IDs.
Example fix
// before
A.setParentId("B"); B.setParentId("A"); // cycle
// after
A.setParentId("B"); B.setParentId(null); // B is root Defensive patterns
Strategy: try-catch
Validate before calling
Set<PK> seen = new HashSet<>(); for (E e : list) { PK p = e.getParentId(); while (p != null) { if (!seen.add(p)) { throw new ValidationException("parentId", "cycle detected at " + p); } p = parentOf(p); } } Try / catch
try { helper.checkCyclicDependency(list); } catch (ValidationException e) { if ("error.tree_entity_cyclic_dependency".equals(e.getCode())) { /* return 400 with detail */ } throw e; } Prevention
- Prevent selecting a node's own subtree as parent in the UI
- Validate parent links before bulk save
- Write integration tests for swap-parent operations
When it happens
Trigger: Saving or re-sorting a tree entity set whose parentId links form a loop, e.g. A.parent=B and B.parent=A, or a node whose parentId chain returns to itself; triggered via the helper's validation entry that seeds checkCyclicDependency for each root.
Common situations: Bulk imports where child rows precede parents with swapped IDs; UI drag-and-drop allowing a node to be dropped into its own subtree; concurrent edits creating inconsistent parent pointers.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- error.tree_entity_parent_id_not_exist
- parentId
- error.page_size_exceeded
- undefined column [" + column + "]
- error.illegal_column_name
AI-assisted analysis of hs-web/hsweb-framework@b2cfc85a57 (2026-09-13).
Data as JSON: /api/errors/edfb512c71fbc49e.
Report an issue: GitHub.
Appendix: source
Thrown at hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/TreeSortServiceHelper.java:136
for (E value : allData.values()) {
if (isRootNode(value) || value.getId() == null) {
continue;
}
childrenMapping
.computeIfAbsent(value.getParentId(), ignore -> new LinkedHashMap<>())
.put(value.getId(), value);
}
}
private void checkCyclicDependency() {
for (E value : readyToSave.values()) {
checkCyclicDependency(value, new LinkedHashSet<>());
}
}
private void checkCyclicDependency(E val, Set<PK> container) {
if (!container.add(val.getId())) {
throw new ValidationException("parentId", "error.tree_entity_cyclic_dependency");
}
Map<PK, E> children = childrenMapping.get(val.getId());
if (MapUtils.isNotEmpty(children)) {
for (Map.Entry<PK, E> entry : children.entrySet()) {
checkCyclicDependency(entry.getValue(), container);
}
}
}
private Mono<Void> checkParentId() {
if (allData.isEmpty()) {
return Mono.empty();
}
Set<PK> readyToCheck = thisTime
.values()
.stream()View on GitHub (pinned to b2cfc85a57)