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
- Refresh the tag list and confirm the _id is present before deleting.
- Treat 'tag-not-found' as an idempotent no-op in the client handler.
- 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
- Guard the UI delete button against double submission.
- Treat removeTag as idempotent on the client.
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
- error-forwarding-department-target-not-allowed
- error-invalid-sla
- error-invalid-priority
- error-invalid-sla
- unit-not-found
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/4f0d37620721e002.
Report an issue: GitHub.