RocketChat/Rocket.Chat · error · Meteor.Error

error-fallback-department-not-found

error-fallback-department-not-found

Error message

Fallback department not found

What it means

Thrown by `livechat:saveDepartment` when `fallbackForwardDepartment` is set and survives the self-reference check, but `LivechatDepartment.findOneById(fallbackForwardDepartment, { projection: { _id: 1 } })` returns null. The referenced fallback department must exist at save time; referential integrity is enforced in application code, not by the schema.

Source

Thrown at apps/meteor/server/lib/omnichannel/departmentsLib.ts:121

	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) {
		await updateDepartmentAgents(departmentDB._id, departmentAgents, departmentDB.enabled);
	}

	if (department?.enabled !== departmentData.enabled) {
		void notifyOnLivechatDepartmentAgentChangedByDepartmentId(departmentDB._id, department ? 'updated' : 'inserted');
	}

	// Disable event
	if (department?.enabled && !departmentDB?.enabled) {
		await callbacks.run('livechat.afterDepartmentDisabled', departmentDB);
		void Apps.self?.triggerEvent(AppEvents.IPostLivechatDepartmentDisabled, { department: departmentDB });

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Re-open the fallback selector and choose an existing department (refresh the list first)
  2. Clear the fallback field if no fallback is needed
  3. When deleting departments programmatically, also update departments that reference the deleted one as fallback to avoid stale ids

Example fix

// before
await saveDepartment(_id, { ...data, fallbackForwardDepartment: 'dep-old' }); // deleted

// after
const fb = await LivechatDepartment.findOneById('dep-old', { projections: { _id: 1 } } as any);
await saveDepartment(_id, { ...data, ...(fb ? { fallbackForwardDepartment: fb._id } : {}) });
Defensive patterns

Strategy: validation

Validate before calling

if (deptData.fallbackForwardDepartment) {
  const fb = await LivechatDepartment.findOneById(deptData.fallbackForwardDepartment, { projection: { _id: 1 } });
  if (!fb) throw new Error('Fallback department does not exist');
}
await saveDepartment(_id, deptData);

Type guard

const fallbackResolvable = async (fallbackId: string): Promise<boolean> =>
  (await LivechatDepartment.findOneById(fallbackId, { projection: { _id: 1 } })) != null;

Try / catch

try {
  await saveDepartment(_id, deptData);
} catch (e) {
  if (isMeteorError(e, 'error-fallback-department-not-found')) {
    // re-pick fallback from a freshly loaded department list
  }
}

Prevention

When it happens

Trigger: Setting a fallback department id that was deleted, is from another environment, or is a typo; deleting a department that others use as fallback and then saving any of those dependents (note removeDepartment only unsets fallback on rooms, so stale references can linger).

Common situations: Deleted fallback targets after department cleanup; ids copied between dev and prod databases; concurrent deletes while an edit form is open.

Related errors


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