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

  1. Fix the offending entities so no parentId chain loops (each node's ancestor chain ends at a root).
  2. In the UI, forbid dropping a node into itself or its descendants.
  3. 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

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


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)