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.
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)
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
- domain_expiry_unsupported_monitor_type
- domain_expiry_unsupported_missing_target
- Service Name is required.
- Invalid service name. Please use the internal Service Name (
- Invalid PM2 process name.
AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12).
Data as JSON: /api/errors/be1f76a6c8c8a4f7.
Report an issue: GitHub.