RocketChat/Rocket.Chat · error · Error
error-invalid-contact
Error message
error-invalid-contact
What it means
Thrown by the mergeContacts patch (runMergeContacts) when LivechatContacts.findOneEnabledById(contactId, { session }) returns null — the supplied contactId does not map to an enabled contact record. Because mergeContacts is a transactional operation that needs a primary contact to merge others into, a missing source is fatal. It is raised inside a ClientSession so it participates in transaction rollback.
Source
Thrown at apps/meteor/ee/server/patches/mergeContacts.ts:20
import { License } from '@rocket.chat/license';
import { LivechatContacts, LivechatRooms, Settings } from '@rocket.chat/models';
import type { ClientSession } from 'mongodb';
import { isSameChannel } from '../../../app/livechat/lib/isSameChannel';
import { notifyOnSettingChanged } from '../../../server/lib/notifyListener';
import { ContactMerger } from '../../../server/lib/omnichannel/contacts/ContactMerger';
import { mergeContacts } from '../../../server/lib/omnichannel/contacts/mergeContacts';
import { contactLogger as logger } from '../lib/omnichannel/logger';
export const runMergeContacts = async (
_next: any,
contactId: string,
visitor: ILivechatContactVisitorAssociation,
session?: ClientSession,
): Promise<ILivechatContact | null> => {
const originalContact = await LivechatContacts.findOneEnabledById(contactId, { session });
if (!originalContact) {
throw new Error('error-invalid-contact');
}
const channel = originalContact.channels.find((channel: ILivechatContactChannel) => isSameChannel(channel.visitor, visitor));
if (!channel) {
throw new Error('error-invalid-channel');
}
logger.debug({ msg: 'Getting similar contacts', contactId });
const similarContacts: ILivechatContact[] = await LivechatContacts.findSimilarVerifiedContacts(channel, contactId, { session });
if (!similarContacts.length) {
logger.debug({ msg: 'No similar contacts found', contactId });
return originalContact;
}
logger.debug({ msg: 'Found contacts to merge', contactId, count: similarContacts.length });
for await (const similarContact of similarContacts) {View on GitHub (pinned to f9d3ec372b)
Solutions
- Re-fetch the contact by id and confirm enabled === true before invoking mergeContacts.
- If the contact no longer exists, abort the merge flow and surface a user-facing 'contact not found' state.
- Make sure the contactId passed is the primary (target) contact, not a similar/secondary one.
- Run the merge inside the same session that loaded the contact to avoid TOCTOU deletion.
Example fix
// before
await mergeContacts(maybeDeletedId, visitor, session);
// after
const contact = await LivechatContacts.findOneEnabledById(contactId, { session });
if (!contact) return null;
await mergeContacts(contactId, visitor, session); Defensive patterns
Strategy: validation
Validate before calling
async function contactEnabled(contactId: string, session?: ClientSession): Promise<boolean> {
const c = await LivechatContacts.findOneEnabledById(contactId, { session });
return Boolean(c);
} Try / catch
try {
await mergeContacts(contactId, visitor, session);
} catch (e) {
if (e.message === 'error-invalid-contact') {
// contact was deleted/disabled — abort merge and notify
} else throw e;
} Prevention
- Load the contact inside the same session you merge in to avoid TOCTOU deletion.
- Surface 'contact not found' to the agent UI when upstream lookups fail.
- Log contactId lineage so concurrent merges are traceable.
When it happens
Trigger: Calling mergeContacts (or the upstream verifyContactChannel flow that triggers it) with a contactId that was deleted, disabled (enabled:false), or never existed; concurrent merge that already deleted this contact.
Common situations: Contact was merged/deleted by another request between lookup and merge; visitor/contact association was cleared; the contactId was extracted from a stale room record; test fixture did not insert the contact.
Related errors
- error-contact-not-found
- error-invalid-channel
- error-verifying-contact-channel
- error-invalid-room
- error-action-not-allowed
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/f1d2c3bc22a5ec3d.
Report an issue: GitHub.