RocketChat/Rocket.Chat · error · Error

error-contact-not-found

error-contact-not-found

Error message

error-contact-not-found

What it means

disableContactById looks the contact up with findOneEnabledById and throws Error('error-contact-not-found') when nothing is returned - meaning the id matches no contact OR matches a contact that is already disabled. All downstream cleanup (guest removal across channels, contact disable) is skipped when this throws.

Source

Thrown at apps/meteor/server/lib/omnichannel/contacts/disableContact.ts:10

import type { ILivechatContact } from '@rocket.chat/core-typings';
import { LivechatContacts, LivechatRooms } from '@rocket.chat/models';

import { settings } from '../../../settings';
import { removeGuest } from '../guests';

export async function disableContactById(contactId: string): Promise<void> {
	const contact = await LivechatContacts.findOneEnabledById<Pick<ILivechatContact, '_id' | 'channels'>>(contactId);
	if (!contact) {
		throw new Error('error-contact-not-found');
	}

	// Checking if the contact has any open channel/room before removing its data.
	const contactOpenRooms = await LivechatRooms.checkContactOpenRooms(contactId);
	if (contactOpenRooms && !settings.get<boolean>('Livechat_Allow_collect_and_store_HTTP_header_informations')) {
		throw new Error('error-contact-has-open-rooms');
	}

	// Cleaning contact/visitor data;
	await Promise.all(contact.channels.map((channel) => removeGuest({ _id: channel.visitor.visitorId })));

	await LivechatContacts.disableByContactId(contactId);
}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Check for an enabled contact first and treat 'already disabled' as success in retry flows
  2. Resolve the correct contact id via search before disabling
  3. Debounce double submissions in the UI/API client

Example fix

// before - throws on the second call
await disableContactById(contactId);

// after - idempotent disable
const enabled = await LivechatContacts.findOneEnabledById(contactId, { projection: { _id: 1 } });
if (enabled) {
  await disableContactById(contactId);
}
Defensive patterns

Strategy: validation

Validate before calling

import { LivechatContacts } from '@rocket.chat/models';

// idempotent disable: only act when an enabled contact exists
const enabled = await LivechatContacts.findOneEnabledById(contactId, { projection: { _id: 1 } });
if (enabled) {
  await disableContactById(contactId);
}

Try / catch

try {
  await disableContactById(contactId);
} catch (err: any) {
  if (err?.message === 'error-contact-not-found') return; // already disabled or never existed
  throw err;
}

Prevention

When it happens

Trigger: Disabling a contact twice (double submit or retry after timeout); a sync job re-processing contacts that were already disabled; passing a wrong or foreign contact id.

Common situations: Retry logic without idempotency awareness in integrations; nightly syncs that do not track disabled state; environment drift of contact ids.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/efdfa320b176a774. Report an issue: GitHub.