RocketChat/Rocket.Chat · error · Error
error-invalid-channel
Error message
error-invalid-channel
What it means
Thrown by runMergeContacts when the original contact exists but none of its channels match the supplied visitor association (isSameChannel returns false for every channel). The merge is driven by a channel, so without a matching channel the code cannot reconcile the visitor with the contact and aborts inside the transaction.
Source
Thrown at apps/meteor/ee/server/patches/mergeContacts.ts:25
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) {
const fields = ContactMerger.getAllFieldsFromContact(similarContact);
await ContactMerger.mergeFieldsIntoContact({ fields, contact: originalContact, session });
}
const similarContactIds = similarContacts.map((c) => c._id);View on GitHub (pinned to f9d3ec372b)
Solutions
- Verify the visitor's source and visitorId match a channel.visitor on the contact before calling mergeContacts.
- If the channel is missing, add/verify it on the contact first via the contact-channel verification flow.
- Ensure isSameChannel semantics (source + visitorId) are consistent with how the channel was originally created.
- Log the supplied visitor vs the contact.channels to find the mismatch.
Defensive patterns
Strategy: validation
Validate before calling
function findMatchingChannel(contact: ILivechatContact, visitor: ILivechatContactVisitorAssociation): ILivechatContactChannel | undefined {
return contact.channels.find((ch) => isSameChannel(ch.visitor, visitor));
}
// if (!findMatchingChannel(contact, visitor)) throw new Error('Visitor does not match any channel'); Try / catch
try {
await mergeContacts(contactId, visitor, session);
} catch (e) {
if (e.message === 'error-invalid-channel') {
// verify/add the channel before retrying the merge
} else throw e;
} Prevention
- Ensure the visitor source matches one of the contact's channel sources before merge.
- Standardize visitorId assignment so reconnects do not break channel matching.
- Add a channel-verification step in the UI before triggering a merge.
When it happens
Trigger: Calling mergeContacts with a visitor whose source/visitorId/token does not line up with any channel.visitor on the contact; visitor came from a different channel type than any channel on the contact.
Common situations: Visitor association built with the wrong source type (e.g. 'api' vs 'web'); visitorId changed after a reconnect; contact channel was unverified/removed; cross-channel merge attempted where no channel overlap exists.
Related errors
- error-invalid-contact
- error-contact-not-found
- error-action-not-allowed
- error-invalid-sla
- error-invalid-user
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/929ab0d868923d7e.
Report an issue: GitHub.