RocketChat/Rocket.Chat · error · Meteor.Error
error-fallback-department-circular
error-fallback-department-circular
Error message
Cannot save department. Circular reference between fallback department and department
What it means
Thrown by `livechat:saveDepartment` when `fallbackForwardDepartment` (the department that receives chats when this one is unavailable) equals the department's own `_id`. A self-referencing fallback would loop a stranded chat back to the same unavailable department, so the save is rejected before persistence.
Source
Thrown at apps/meteor/server/lib/omnichannel/departmentsLib.ts:110
);
const { requestTagBeforeClosingChat, chatClosingTags, fallbackForwardDepartment } = departmentData;
if (requestTagBeforeClosingChat && (!chatClosingTags || chatClosingTags.length === 0)) {
throw new Meteor.Error(
'error-validating-department-chat-closing-tags',
'At least one closing tag is required when the department requires tag(s) on closing conversations.',
{ method: 'livechat:saveDepartment' },
);
}
if (_id && !department) {
throw new Meteor.Error('error-department-not-found', 'Department not found', {
method: 'livechat:saveDepartment',
});
}
if (fallbackForwardDepartment === _id) {
throw new Meteor.Error(
'error-fallback-department-circular',
'Cannot save department. Circular reference between fallback department and department',
);
}
if (fallbackForwardDepartment) {
const fallbackDep = await LivechatDepartment.findOneById<Pick<ILivechatDepartment, '_id'>>(fallbackForwardDepartment, {
projection: { _id: 1 },
});
if (!fallbackDep) {
throw new Meteor.Error('error-fallback-department-not-found', 'Fallback department not found', {
method: 'livechat:saveDepartment',
});
}
}
const departmentDB = await LivechatDepartment.createOrUpdateDepartment(_id, departmentData);
if (departmentDB && departmentAgents) {View on GitHub (pinned to b2c16d5842)
Solutions
- Pick a different department as fallback, or clear the field entirely (falsy fallbackForwardDepartment skips both the circular and existence checks)
- Fix form logic so a department is never offered as its own fallback option
Example fix
// before
await saveDepartment(_id, { ...data, fallbackForwardDepartment: _id });
// after
const payload = { ...data };
if (payload.fallbackForwardDepartment === _id) {
delete payload.fallbackForwardDepartment;
}
await saveDepartment(_id, payload); Defensive patterns
Strategy: validation
Validate before calling
if (deptData.fallbackForwardDepartment && deptData.fallbackForwardDepartment === _id) {
delete deptData.fallbackForwardDepartment; // or reject with a clear message
}
await saveDepartment(_id, deptData); Type guard
const hasCircularFallback = (_id: string, fallback?: string): boolean => fallback != null && fallback === _id;
Try / catch
try {
await saveDepartment(_id, deptData);
} catch (e) {
if (isMeteorError(e, 'error-fallback-department-circular')) {
// clear the fallback selection and re-save
}
} Prevention
- Exclude the current department from its own fallback dropdown options
- Assert fallback !== self in API clients before sending
- Treat fallback as optional: omit rather than default to self
When it happens
Trigger: The department edit form pre-populates the fallback dropdown with the department itself and the user leaves it selected; or an API client builds the payload with `fallbackForwardDepartment: _id` by default when no other fallback is chosen.
Common situations: Default-select behavior in custom dropdowns; scripted bulk edits that set fallback to the department's own id; misunderstanding the field as 'primary department' rather than 'fallback target'.
Related errors
- error-validating-department-chat-closing-tags
- error-fallback-department-not-found
- Invalid type
- error-forwarding-department-target-not-allowed
- error-duplicated-sla
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/23b200dfec057cc8.
Report an issue: GitHub.