RocketChat/Rocket.Chat · error · Error
error-invalid-contact
error-invalid-contact
Error message
error-invalid-contact
What it means
The enterprise patch runMergeContacts (apps/meteor/ee/server/patches/mergeContacts.ts:20) overrides contact merging: the contact to merge into must exist and be enabled (LivechatContacts.findOneEnabledById). A contactId that is unknown, deleted, or already disabled — merging into another contact disables the source record — throws error-invalid-contact.
Solutions
- Verify the contact is still enabled before merging: LivechatContacts.findOneEnabledById(contactId)
- If the contact was already merged, retarget the merge at the surviving (enabled) contact
- Make merge flows idempotent: treat error-invalid-contact as 'already merged/deleted' success
- Inspect the contact registry to find which record absorbed it
Example fix
// before
await mergeContacts(contactId, visitor);
// after
const contact = await LivechatContacts.findOneEnabledById(contactId);
if (!contact) {
// already merged or deleted — find the surviving contact or skip
return null;
}
await mergeContacts(contactId, visitor); Defensive patterns
Strategy: validation
Validate before calling
const contact = await LivechatContacts.findOneEnabledById(contactId);
if (!contact) {
// contact missing or already merged/disabled — retarget or skip
return null;
}
return mergeContacts(contactId, visitor); Type guard
const isMergeableContact = (contact: ILivechatContact | null): contact is ILivechatContact => !!contact && contact.disabled !== true;
Try / catch
try {
return await mergeContacts(contactId, visitor);
} catch (e) {
if (e instanceof Error && e.message === 'error-invalid-contact') {
// contact already merged/disabled: find the surviving contact and merge into it instead
}
throw e;
} Prevention
- Verify the contact is enabled immediately before merging
- Make merge flows idempotent: a second merge attempt against the same contact should resolve to the surviving record
- In channel/webhook handlers, look contacts up by verified channel rather than persisting contact ids
When it happens
Trigger: Calling mergeContacts(contactId, visitor, session?) where contactId no longer resolves to an enabled contact: the contact was already merged (and thus disabled), deleted, or the id is wrong.
Common situations: Retrying a merge whose first attempt already disabled the contact; concurrent merges targeting the same contact; stale contact ids held by channel webhooks or integrations; contacts disabled by a previous merge operation.
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
- error-contact-not-found
- error-contact-not-found
- error-contact-not-found
- error-contact-not-found
- error-contact-not-found
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/f1d2c3bc22a5ec3d.
Report an issue: GitHub.
Appendix: 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 b2c16d5842)