louislam/uptime-kuma · error · Error
Invalid Monitor Group
Error message
Invalid Monitor Group
What it means
Thrown by 'editMonitor' when monitor.parent is not null AND that parent ID appears among the monitor's own descendants (Monitor.getAllChildrenIDs(monitor.id)). This prevents reparenting a monitor under one of its own descendants, which would create a cycle and an endless loop in tree traversal.
Solutions
- Pick a parent that is not among the monitor's descendants (verify against getAllChildrenIDs before submitting).
- Disable descendant monitors in the parent-picker dropdown of the UI.
- If restructuring a subtree, move the would-be parent up the tree first to break the chain.
- Catch 'Invalid Monitor Group' and surface a clear message to re-select the parent.
Example fix
// before
socket.emit('editMonitor', { id, parent: candidateParent }, cb);
// after
const descendantIds = await getAllChildrenIDs(id);
if (descendantIds.includes(candidateParent)) {
return error('Cannot set a descendant as parent');
}
socket.emit('editMonitor', { id, parent: candidateParent }, cb); Defensive patterns
Strategy: validation
Validate before calling
// Reject cycle-creating parents before submitting
const descendantIds = await getAllChildrenIDs(monitor.id);
if (monitor.parent != null && descendantIds.includes(Number(monitor.parent))) {
return setError('Cannot set a descendant monitor as the parent.');
} Type guard
function isAcyclicParent(parentId, descendantIds) {
return parentId == null || (!descendantIds.includes(Number(parentId)) && Number(parentId) !== Number(monitorId));
} Prevention
- Compute descendants and exclude them (and self) from the parent picker.
- Catch 'Invalid Monitor Group' and prompt the user to re-select a parent.
- When restructuring subtrees, move parents up the tree first.
- Validate ancestry client-side before emitting editMonitor.
When it happens
Trigger: Reorganizing monitor groups so that a parent monitor is moved beneath one of its current descendants. The cycle check fires before the edit is applied.
Common situations: Drag-and-drop group reordering that creates a cycle; bulk reparenting tool that does not check ancestry; UI that allows selecting any monitor as a parent.
Related errors
- Accepted status codes are not all strings
- domain_expiry_unsupported_missing_target
- domain_expiry_unsupported_monitor_type
- Echo requests count must be between
- Empty or undefined response. Check query syntax and…
AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12).
Data as JSON: /api/errors/be1f76a6c8c8a4f7.
Report an issue: GitHub.
Appendix: source
Thrown at server/server.js:833
});
// Edit a monitor
socket.on("editMonitor", async (monitor, callback) => {
try {
let removeGroupChildren = false;
checkLogin(socket);
let bean = await R.findOne("monitor", " id = ? ", [monitor.id]);
if (bean.user_id !== socket.userID) {
throw new Error("Permission denied.");
}
// Check if Parent is Descendant (would cause endless loop)
if (monitor.parent !== null) {
const childIDs = await Monitor.getAllChildrenIDs(monitor.id);
if (childIDs.includes(monitor.parent)) {
throw new Error("Invalid Monitor Group");
}
}
// Remove children if monitor type has changed (from group to non-group)
if (bean.type === "group" && monitor.type !== bean.type) {
removeGroupChildren = true;
}
// Ensure status code ranges are strings
if (!monitor.accepted_statuscodes.every((code) => typeof code === "string")) {
throw new Error("Accepted status codes are not all strings");
}
bean.name = monitor.name;
bean.description = monitor.description;
bean.parent = monitor.parent;
bean.type = monitor.type;
bean.subtype = monitor.subtype;View on GitHub (pinned to 6b5ea01557)