halo-dev/halo · error · ServerWebInputException
Target parent has a cyclic parent chain.
Error message
Target parent has a cyclic parent chain.
What it means
MenuItemConsoleService.isDescendant performs the same visited-set cycle detection as the category service, but for menu items (walking parent menu items via parentNameOf). It throws ServerWebInputException (HTTP 400) 'Target parent has a cyclic parent chain.' when the menu item hierarchy already loops, to prevent reparenting an item under its own descendant or operating on already-corrupted data.
Source
Thrown at application/src/main/java/run/halo/app/core/endpoint/console/MenuItemConsoleService.java:242
.map(MenuItem.MenuItemSpec::getParent)
.orElse(null));
}
private static String menuNameOf(MenuItem menuItem) {
return Optional.ofNullable(menuItem.getSpec())
.map(MenuItem.MenuItemSpec::getMenuName)
.orElse(null);
}
private static boolean isDescendant(String candidateName, String ancestorName, Map<String, MenuItem> itemMap) {
var current = candidateName;
var visited = new HashSet<String>();
while (current != null) {
if (Objects.equals(current, ancestorName)) {
return true;
}
if (!visited.add(current)) {
throw new ServerWebInputException("Target parent has a cyclic parent chain.");
}
current = Optional.ofNullable(itemMap.get(current))
.map(MenuItemConsoleService::parentNameOf)
.orElse(null);
}
return false;
}
private static List<MenuItem> siblings(List<MenuItem> items, String parentName, String excludingName) {
return items.stream()
.filter(item -> !Objects.equals(item.getMetadata().getName(), excludingName))
.filter(item -> Objects.equals(parentNameOf(item), parentName))
.sorted(defaultMenuItemComparator())
.collect(Collectors.toCollection(ArrayList::new));
}
private static int indexOf(List<MenuItem> menuItems, String name) {
for (int i = 0; i < menuItems.size(); i++) {View on GitHub (pinned to d2f5165f9c)
Solutions
- Inspect and repair the menu item hierarchy to remove any existing loop.
- Do not reparent an item under one of its descendants.
- Serialize menu hierarchy edits to avoid races forming cycles.
- Validate imported menu data for cycles before applying.
Defensive patterns
Strategy: validation
Validate before calling
if (isDescendant(newParentName, item.getMetadata().getName(), itemMap)) {
return Mono.error(new ServerWebInputException("Target parent has a cyclic parent chain."));
} Type guard
static boolean hasNoMenuItemCycle(Map<String, MenuItem> map, String start) {
var visited = new HashSet<String>();
var cur = start;
while (cur != null) {
if (!visited.add(cur)) return false;
cur = Optional.ofNullable(map.get(cur))
.map(MenuItemConsoleService::parentNameOf).orElse(null);
}
return true;
} Prevention
- Serialize menu hierarchy edits to avoid races.
- Never reparent a menu item under one of its descendants.
- Validate imported menu data for cycles before applying.
When it happens
Trigger: Reparenting a menu item such that the candidate parent chain revisits a node; operating on a menu whose items already form a loop due to prior corruption or racing writes.
Common situations: Concurrent menu restructuring requests racing; direct edits to menu item spec parent/priority creating a loop; import of menu data that contained a cycle.
Related errors
- Target parent has a cyclic parent chain.
- menuName is required.
- The required parameter 'commentName' is missing.
- urls must not be empty.
- url must not be blank.
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/997603f8ae12b043.
Report an issue: GitHub.