{"record":{"id":"437e33311ac7b33f","repo":"RocketChat/Rocket.Chat","slug":"error-verifying-contact-channel","errorCode":"error-verifying-contact-channel","errorMessage":"error-verifying-contact-channel","messagePattern":"error-verifying-contact-channel","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/ee/server/patches/verifyContactChannel.ts","lineNumber":53,"sourceCode":"\t\tawait LivechatContacts.updateFromUpdaterByAssociation({ visitorId, source: room.source }, updater, { session });\n\n\t\tawait LivechatRooms.update({ _id: roomId }, { $set: { verified: true } }, { session });\n\t\tlogger.debug({ msg: 'Merging contacts', contactId, visitorId, roomId });\n\n\t\tconst mergeContactsResult = await mergeContacts(contactId, { visitorId, source: room.source }, session);\n\n\t\tawait session.commitTransaction();\n\n\t\treturn mergeContactsResult;\n\t} catch (e) {\n\t\tawait session.abortTransaction();\n\t\tif (shouldRetryTransaction(e) && attempts > 0) {\n\t\t\tlogger.debug({ msg: 'Retrying to verify contact channel', contactId, visitorId, roomId });\n\t\t\treturn _verifyContactChannel(params, room, attempts - 1);\n\t\t}\n\n\t\tlogger.error({ msg: 'Error verifying contact channel', contactId, visitorId, roomId, error: e });\n\t\tthrow new Error('error-verifying-contact-channel');\n\t} finally {\n\t\tawait session.endSession();\n\t}\n}\n\nexport const runVerifyContactChannel = async (\n\t_next: any,\n\tparams: {\n\t\tcontactId: string;\n\t\tfield: string;\n\t\tvalue: string;\n\t\tvisitorId: string;\n\t\troomId: string;\n\t},\n): Promise<ILivechatContact | null> => {\n\tconst { roomId, contactId, visitorId } = params;\n\n\tconst room = await LivechatRooms.findOneById(roomId);","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/patches/verifyContactChannel.ts#L35-L71","documentation":"Catch-all thrown by _verifyContactChannel in the EE contact-verification patch when the Mongo transaction that verifies a channel and merges contacts fails. The session is aborted, retryable errors (shouldRetryTransaction, e.g. TransientTransactionError) are retried with a decreasing attempt counter, and when retries are exhausted or the error is not retryable, the root cause is logged (msg: 'Error verifying contact channel', with contactId/visitorId/roomId/error) and this generic error is rethrown.","triggerScenarios":"Any exception inside the verify+merge transaction: write conflicts from two agents/visitors merging the same contact concurrently, replica set step-down, Mongo deployed without transaction support (standalone mongod), or invalid contact/channel state surfacing mid-transaction after the built-in retry budget is spent.","commonSituations":"Concurrent verification of the same visitor from two rooms, MongoDB not running as a replica set, network blips during commit, transaction exceeding timeouts.","solutions":["Read the server log entry 'Error verifying contact channel' — the underlying exception is logged there; fix that root cause first.","Confirm MongoDB runs as a replica set (transactions are unsupported on standalone mongod).","Retry the whole verify operation after a short backoff — the transaction aborted, so no partial state was left behind.","If the error is deterministic, inspect the contact/channel documents for corrupt or unexpected state."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// environment pre-check: Mongo transactions need a replica set\nconst hello = await db.admin().command({ hello: 1 });\nif (!hello.setName) {\n\tthrow new Error('verifyContactChannel requires a MongoDB replica set');\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait runVerifyContactChannel(null, { contactId, field, value, visitorId, roomId });\n} catch (e) {\n\tif (e instanceof Error && e.message === 'error-verifying-contact-channel') {\n\t\tawait new Promise((r) => setTimeout(r, backoffMs)); // let concurrent merges finish\n\t\treturn runVerifyContactChannel(null, { contactId, field, value, visitorId, roomId }); // safe: prior txn aborted atomically\n\t}\n\tthrow e;\n}","preventionTips":["Run MongoDB as a replica set so transactions are available","Queue or lock verification per visitor to avoid write conflicts between concurrent merges","Alert on the logged root-cause error ('Error verifying contact channel' log line), not the wrapper error"],"tags":["livechat","contacts","mongodb","transactions","enterprise"],"backgroundTag":"transaction-aborted","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}