{"record":{"id":"01f70bf7efd96160","repo":"RocketChat/Rocket.Chat","slug":"error-contact-not-found-01f70b","errorCode":"error-contact-not-found","errorMessage":"error-contact-not-found","messagePattern":"error-contact-not-found","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/omnichannel/contacts/resolveContactConflicts.ts","lineNumber":27,"sourceCode":"\tcontactId: string;\n\tname?: string;\n\tcustomFields?: Record<string, unknown>;\n\tcontactManager?: string;\n\twipeConflicts?: boolean;\n};\n\nexport async function resolveContactConflicts(params: ResolveContactConflictsParams): Promise<ILivechatContact> {\n\tconst { contactId, name, customFields, contactManager, wipeConflicts } = params;\n\n\tconst contact = await LivechatContacts.findOneEnabledById<Pick<ILivechatContact, '_id' | 'customFields' | 'conflictingFields'>>(\n\t\tcontactId,\n\t\t{\n\t\t\tprojection: { _id: 1, customFields: 1, conflictingFields: 1 },\n\t\t},\n\t);\n\n\tif (!contact) {\n\t\tthrow new Error('error-contact-not-found');\n\t}\n\n\tif (!contact.conflictingFields?.length) {\n\t\tthrow new Error('error-contact-has-no-conflicts');\n\t}\n\n\tif (contactManager) {\n\t\tawait validateContactManager(contactManager);\n\t}\n\n\tlet updatedConflictingFieldsArr: ILivechatContactConflictingField[] = [];\n\tif (wipeConflicts) {\n\t\tconst value = await Settings.incrementValueById('Resolved_Conflicts_Count', contact.conflictingFields.length, {\n\t\t\treturnDocument: 'after',\n\t\t});\n\t\tif (value) {\n\t\t\tvoid notifyOnSettingChanged(value);\n\t\t}","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/omnichannel/contacts/resolveContactConflicts.ts#L9-L45","documentation":"resolveContactConflicts (POST /api/v1/omnichannel/contact.resolveConflicts) loads the contact with LivechatContacts.findOneEnabledById; if no enabled contact matches contactId, this plain Error with the code as its message is thrown. 'Not found' here covers both a wrong _id and a contact that exists but is disabled/deleted.","triggerScenarios":"Calling contact.resolveConflicts with a contactId that is mistyped, belongs to a deleted contact, or to a contact whose 'enabled' flag is false (findOneEnabledById filters disabled contacts).","commonSituations":"Contact was deleted or disabled by another process between listing and resolving; client copied the id from an outdated export; id truncated when hand-copied.","solutions":["Fetch the contact first (GET /api/v1/omnichannel/contact) and confirm it returns and is enabled","Re-list contacts and copy the current _id; ids are 17-char Mongo ObjectIDs and easy to truncate","If the contact was disabled intentionally, resolve conflicts on the active merged/primary contact instead"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const contact = await LivechatContacts.findOneEnabledById(contactId, { projection: { _id: 1 } });\nif (!contact) {\n  throw new Error(`No enabled contact with id ${contactId}`);\n}\nawait resolveContactConflicts(params);","typeGuard":"const isValidContactId = (id: unknown): id is string =>\n  typeof id === 'string' && /^[0-9a-f]{17}$/i.test(id);","tryCatchPattern":"try {\n  await resolveContactConflicts(params);\n} catch (err) {\n  if (err instanceof Error && err.message === 'error-contact-not-found') {\n    // re-fetch contact list; id is wrong, stale, or contact is disabled\n  }\n  throw err;\n}","preventionTips":["Always copy contactId from a live API response, never from stale exports","Remember findOneEnabledById excludes disabled contacts — a 200 on GET does not guarantee enabled","Log contactIds at submit time to debug copy/truncation issues fast"],"tags":["omnichannel","livechat","contacts","conflicts","not-found","validation"],"backgroundTag":"resource-not-found","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}