{"record":{"id":"8813db92b60e9e5d","repo":"RocketChat/Rocket.Chat","slug":"error-contact-not-found-8813db","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/getContactHistory.ts","lineNumber":38,"sourceCode":"\t}: {\n\t\tcontactId: string;\n\t\toptions?: FindOptions<IOmnichannelRoom>;\n\t\textraParams?: Record<string, any>;\n\t}): Promise<FindPaginated<FindCursor<IOmnichannelRoom>>> =>\n\t\tLivechatRooms.findClosedRoomsByContactPaginated({\n\t\t\tcontactId,\n\t\t\toptions,\n\t\t}),\n);\n\nexport const getContactHistory = makeFunction(\n\tasync (params: GetContactHistoryParams): Promise<PaginatedResult<{ history: VisitorSearchChatsResult[] }>> => {\n\t\tconst { contactId, count, offset, sort } = params;\n\n\t\tconst contact = await LivechatContacts.findOneEnabledById<Pick<ILivechatContact, '_id'>>(contactId, { projection: { _id: 1 } });\n\n\t\tif (!contact) {\n\t\t\tthrow new Error('error-contact-not-found');\n\t\t}\n\n\t\tconst options: FindOptions<IOmnichannelRoom> = {\n\t\t\tsort: sort || { closedAt: -1 },\n\t\t\tskip: offset,\n\t\t\tlimit: count,\n\t\t\tprojection: {\n\t\t\t\tfname: 1,\n\t\t\t\tts: 1,\n\t\t\t\tv: 1,\n\t\t\t\tmsgs: 1,\n\t\t\t\tservedBy: 1,\n\t\t\t\tclosedAt: 1,\n\t\t\t\tclosedBy: 1,\n\t\t\t\tcloser: 1,\n\t\t\t\ttags: 1,\n\t\t\t\tsource: 1,\n\t\t\t\tlastMessage: 1,","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/omnichannel/contacts/getContactHistory.ts#L20-L56","documentation":"getContactHistory resolves the contact with findOneEnabledById (projecting only _id) before querying its chat history, and throws Error('error-contact-not-found') when the id matches no enabled contact. Deleted and disabled contacts therefore have no retrievable history through this path.","triggerScenarios":"Calling the contact-history endpoint/method for a deleted contact, a contact already disabled (e.g. after a privacy wipe), or a plain invalid id.","commonSituations":"UI listings showing stale contacts after a concurrent disable; pagination cursors surviving past deletion; token/id confusion common to the other contact endpoints.","solutions":["Verify the contact exists and is enabled before requesting its history","If the contact was disabled, surface 'history unavailable' rather than retrying","Refresh contact lists after disable/delete operations so stale ids are not queried"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import { LivechatContacts } from '@rocket.chat/models';\n\nconst contact = await LivechatContacts.findOneEnabledById(contactId, { projection: { _id: 1 } });\nif (!contact) {\n  // skip the history call: contact deleted or disabled - respond 'history unavailable'\n}","typeGuard":null,"tryCatchPattern":"try {\n  const history = await getContactHistory({ contactId, count, offset });\n} catch (err: any) {\n  if (err?.message === 'error-contact-not-found') return respondNotFound(contactId);\n  throw err;\n}","preventionTips":["Refresh contact listings after disable/delete operations so stale ids are not queried","Gate history views on the contact's enabled state","Handle 'not found' as a normal, non-retryable outcome"],"tags":["omnichannel","contacts","history","not-found"],"backgroundTag":"contact-not-found","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}