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

  1. Pick a different department as fallback, or clear the field entirely (falsy fallbackForwardDepartment skips both the circular and existence checks)
  2. 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

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


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/23b200dfec057cc8. Report an issue: GitHub.