RocketChat/Rocket.Chat · error · Error
error-contact-not-found
error-contact-not-found
Error message
error-contact-not-found
What it means
updateContact (PUT /api/v1/omnichannel/contact) loads the contact via LivechatContacts.findOneEnabledById with only _id, name, customFields, conflictingFields projected. If contactId does not match an enabled contact, this error is thrown before any validation of the update payload. Both unknown ids and disabled contacts land here.
Source
Thrown at apps/meteor/server/lib/omnichannel/contacts/updateContact.ts:37
phones?: string[];
customFields?: Record<string, unknown>;
contactManager?: string;
channels?: ILivechatContactChannel[];
wipeConflicts?: boolean;
};
export async function updateContact(params: UpdateContactParams): Promise<ILivechatContact> {
const { contactId, name, emails, phones, customFields: receivedCustomFields, contactManager, channels, wipeConflicts } = params;
const contact = await LivechatContacts.findOneEnabledById<Pick<ILivechatContact, '_id' | 'name' | 'customFields' | 'conflictingFields'>>(
contactId,
{
projection: { _id: 1, name: 1, customFields: 1, conflictingFields: 1 },
},
);
if (!contact) {
throw new Error('error-contact-not-found');
}
if (contactManager) {
await validateContactManager(contactManager);
}
if (wipeConflicts && contact.conflictingFields?.length) {
const value = await Settings.incrementValueById('Resolved_Conflicts_Count', contact.conflictingFields.length, {
returnDocument: 'after',
});
if (value) {
void notifyOnSettingChanged(value);
}
}
const workspaceAllowedCustomFields = await getAllowedCustomFields();
const workspaceAllowedCustomFieldsIds = workspaceAllowedCustomFields.map((customField) => customField._id);
const currentCustomFieldsIds = Object.keys(contact.customFields || {});View on GitHub (pinned to b2c16d5842)
Solutions
- GET /api/v1/omnichannel/contact with the same contactId to confirm it exists and is enabled
- Make sure you pass the contact _id (from the register/list response), not the visitor token or visitorId
- If the contact was disabled, find and update the active contact that replaced it
Defensive patterns
Strategy: validation
Validate before calling
const contact = await LivechatContacts.findOneEnabledById(contactId, { projection: { _id: 1 } });
if (!contact) throw new Error(`No enabled contact ${contactId}`);
await updateContact(params); Type guard
const isContactId = (v: unknown): v is string => typeof v === 'string' && /^[0-9a-f]{17}$/i.test(v) && v !== token && v !== visitorId; Try / catch
try {
await updateContact(params);
} catch (err) {
if (err instanceof Error && err.message === 'error-contact-not-found') {
// verify id source: must be contact._id, not token/visitorId; check contact not disabled
}
throw err;
} Prevention
- Store the contactId returned by registerContact, distinct from visitor token/id
- Validate id shape client-side (17-hex ObjectID)
- Refresh contact ids after merges or imports
When it happens
Trigger: PUT /omnichannel/contact with a contactId that is wrong, stale, or refers to a contact with enabled:false; also triggered when a client sends the visitor token or room id where the contact _id is expected.
Common situations: Client persists a contactId across restarts and the contact was since deleted; contact disabled by a merge process; confusion between visitorId/token and contactId in integrations.
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-visitor-not-found
- error-contact-not-found
- error-contact-manager-not-found
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/fcdfd66cd3bb0c67.
Report an issue: GitHub.