RocketChat/Rocket.Chat · error · Meteor.Error

tag-not-found

tag-not-found

Error message

Tag not found

What it means

removeTag loads the livechat tag by id before deleting and running the livechat.afterTagRemoved callback. If no tag matches the id, it throws tag-not-found for method livechat:removeTag.

Solutions

  1. Refresh the tags list and only delete ids that still appear
  2. Make delete flows idempotent: treat tag-not-found as success when the goal is absence
  3. Verify the id via the tag list endpoint before deleting
Defensive patterns

Strategy: try-catch

Validate before calling

const tag = await LivechatTag.findOneById(_id, { projection: { _id: 1 } });
if (!tag) {
	// already removed; nothing to do
}
await LivechatEnterprise.removeTag(_id);

Try / catch

try {
	await LivechatEnterprise.removeTag(_id);
} catch (err) {
	if ((err as Meteor.Error)?.error === 'tag-not-found') {
		// treat as success: the tag is gone
	} else {
		throw err;
	}
}

Prevention

When it happens

Trigger: Calling livechat:removeTag with a deleted or never-existing tag id; double-delete from concurrent UI actions.

Common situations: Two admins deleting the same tag; stale tag list after removal; automation re-running a delete step.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at apps/meteor/ee/server/lib/omnichannel/LivechatEnterprise.ts:137

		const validUserMonitors = await Users.findUsersInRolesWithQuery(
			'livechat-monitor',
			{ _id: { $in: unitMonitors.map(({ monitorId }) => monitorId) } },
			{ projection: { _id: 1, username: 1 } },
		).toArray();

		const monitors = validUserMonitors.map(({ _id: monitorId, username }) => ({
			monitorId,
			username: username!,
		}));

		return LivechatUnit.createOrUpdateUnit(_id, unitData, ancestors, monitors, unitDepartments);
	},

	async removeTag(_id: string) {
		const tag = await LivechatTag.findOneById<Pick<ILivechatTag, '_id' | 'name'>>(_id, { projection: { _id: 1, name: 1 } });

		if (!tag) {
			throw new Meteor.Error('tag-not-found', 'Tag not found', { method: 'livechat:removeTag' });
		}

		await callbacks.run('livechat.afterTagRemoved', tag);
		return LivechatTag.removeById(_id);
	},

	async saveTag(_id: string | undefined, tagData: { name: string; description?: string }, tagDepartments: string[] | undefined) {
		return LivechatTag.createOrUpdateTag(_id, tagData, tagDepartments);
	},

	async saveSLA(
		_id: string | null,
		slaData: Pick<IOmnichannelServiceLevelAgreements, 'name' | 'description' | 'dueTimeInMinutes'>,
		executedBy: string,
	) {
		const oldSLA =
			_id &&
			(await OmnichannelServiceLevelAgreements.findOneById<Pick<IOmnichannelServiceLevelAgreements, 'dueTimeInMinutes'>>(_id, {

View on GitHub (pinned to b2c16d5842)