RocketChat/Rocket.Chat · warning · Error

error-contact-has-no-conflicts

error-contact-has-no-conflicts

Error message

error-contact-has-no-conflicts

What it means

resolveContactConflicts refuses to run when the contact exists but contact.conflictingFields is empty or unset. Conflict resolution only makes sense when the contact actually has conflicting field values recorded (they are created by updateContactsCustomFields when overwrite is false). Calling resolve on a clean contact is a state error, not a data corruption.

Source

Thrown at apps/meteor/server/lib/omnichannel/contacts/resolveContactConflicts.ts:31

	wipeConflicts?: boolean;
};

export async function resolveContactConflicts(params: ResolveContactConflictsParams): Promise<ILivechatContact> {
	const { contactId, name, customFields, contactManager, wipeConflicts } = params;

	const contact = await LivechatContacts.findOneEnabledById<Pick<ILivechatContact, '_id' | 'customFields' | 'conflictingFields'>>(
		contactId,
		{
			projection: { _id: 1, customFields: 1, conflictingFields: 1 },
		},
	);

	if (!contact) {
		throw new Error('error-contact-not-found');
	}

	if (!contact.conflictingFields?.length) {
		throw new Error('error-contact-has-no-conflicts');
	}

	if (contactManager) {
		await validateContactManager(contactManager);
	}

	let updatedConflictingFieldsArr: ILivechatContactConflictingField[] = [];
	if (wipeConflicts) {
		const value = await Settings.incrementValueById('Resolved_Conflicts_Count', contact.conflictingFields.length, {
			returnDocument: 'after',
		});
		if (value) {
			void notifyOnSettingChanged(value);
		}
	} else {
		const fieldsToRemove = new Set<string>(
			[
				name && 'name',

View on GitHub (pinned to b2c16d5842)

Solutions

  1. GET the contact and check conflictingFields is non-empty before showing/calling resolve
  2. If conflicts were already resolved, treat this as success (idempotent update) rather than retrying
  3. Disable the resolve action in the UI while a resolve request is in flight
Defensive patterns

Strategy: validation

Validate before calling

const contact = await LivechatContacts.findOneEnabledById(contactId, { projection: { conflictingFields: 1 } });
if (!contact?.conflictingFields?.length) {
  // nothing to resolve; treat as no-op success
  return contact;
}
await resolveContactConflicts(params);

Type guard

const hasConflicts = (c: { conflictingFields?: unknown[] } | null): c is { conflictingFields: NonEmptyArray } =>
  Array.isArray(c?.conflictingFields) && (c.conflictingFields as unknown[]).length > 0;

Try / catch

try {
  await resolveContactConflicts(params);
} catch (err) {
  if (err instanceof Error && err.message === 'error-contact-has-no-conflicts') {
    // already resolved elsewhere: refresh UI and continue, do not retry
  }
  throw err;
}

Prevention

When it happens

Trigger: POST /api/v1/omnichannel/contact.resolveConflicts against a contact whose conflictingFields array is empty — e.g. conflicts were already resolved/wiped by a previous call, or the contact never had conflicting writes.

Common situations: Double-submitting the resolve form; a background job and a user both resolving at once; UI not refreshing after a successful resolve so the button is pressed again.

Related errors


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