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

  1. Verify the contact is still enabled before merging: LivechatContacts.findOneEnabledById(contactId)
  2. If the contact was already merged, retarget the merge at the surviving (enabled) contact
  3. Make merge flows idempotent: treat error-invalid-contact as 'already merged/deleted' success
  4. 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

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


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)