RocketChat/Rocket.Chat · error · Meteor.Error

tag-not-found

tag-not-found

Error message

Tag not found

What it means

Thrown by removeTag in LivechatEnterprise.ts:137 when no LivechatTag row matches the supplied _id (findOneById with name projection returns null). The tag is fetched before deletion so the afterTagRemoved callback can receive the full tag object. Meteor.Error 'tag-not-found' with method 'livechat:removeTag'.

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 f9d3ec372b)

Solutions

  1. Refresh the tag list and confirm the _id is present before deleting.
  2. Treat 'tag-not-found' as an idempotent no-op in the client handler.
  3. Guard the UI delete button against double-submission.

Example fix

// before
await removeTag(_id);

// after
try { await removeTag(_id); }
catch (e) {
  if (e instanceof Meteor.Error && e.error === 'tag-not-found') return;
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const tag = await LivechatTag.findOneById(_id, { projection: { _id: 1 } });
if (!tag) return; // already gone

Type guard

const tagExists = async (_id: string) => Boolean(await LivechatTag.findOneById(_id, { projection: { _id: 1 } }));

Try / catch

try { await removeTag(_id); }
catch (e) {
  if (e instanceof Meteor.Error && e.error === 'tag-not-found') return;
  throw e;
}

Prevention

When it happens

Trigger: Calling livechat:removeTag with an _id that was already deleted, or a malformed/non-existent id. Concurrent deletion between two admins also produces it.

Common situations: Stale tag-management UI; double-click on delete; CI test that deletes the same tag twice.

Related errors


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