{"record":{"id":"6a7de50f6b8db40a","repo":"RocketChat/Rocket.Chat","slug":"error-taking-inquiry-lockedat-mismatch","errorCode":"error-taking-inquiry-lockedAt-mismatch","errorMessage":"error-taking-inquiry-lockedAt-mismatch","messagePattern":"error-taking-inquiry-lockedAt-mismatch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/omnichannel/RoutingManager.ts","lineNumber":308,"sourceCode":"\t\t\t\t}\n\t\t\t\tagent = null;\n\t\t\t}\n\t\t}\n\n\t\tif (!agent) {\n\t\t\tlogger.debug({ msg: 'Cannot take inquiry. Precondition failed for agent', inquiryId: inquiry._id });\n\t\t\tconst cbRoom = await callbacks.run<'livechat.onAgentAssignmentFailed'>('livechat.onAgentAssignmentFailed', room, {\n\t\t\t\tinquiry,\n\t\t\t\toptions,\n\t\t\t});\n\t\t\treturn cbRoom;\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await LivechatInquiry.takeInquiry(_id, inquiry.lockedAt);\n\t\t\tif (result.modifiedCount === 0) {\n\t\t\t\tlogger.error({ msg: 'Failed to take inquiry because lockedAt did not match', inquiryId: _id, lockedAt: inquiry.lockedAt });\n\t\t\t\tthrow new Error('error-taking-inquiry-lockedAt-mismatch');\n\t\t\t}\n\n\t\t\tlogger.info({ msg: 'Inquiry taken', inquiryId: _id, agentId: agent.agentId });\n\n\t\t\t// assignAgent changes the room data to add the agent serving the conversation. afterTakeInquiry expects room object to be updated\n\t\t\tconst { inquiry: returnedInquiry, user } = await this.assignAgent(inquiry, agent);\n\t\t\tconst roomAfterUpdate = await LivechatRooms.findOneById(rid);\n\n\t\t\tif (!roomAfterUpdate) {\n\t\t\t\t// This should never happen\n\t\t\t\tthrow new Error('error-room-not-found');\n\t\t\t}\n\n\t\t\tvoid Apps.self?.triggerEvent(AppEvents.IPostLivechatAgentAssigned, { room: roomAfterUpdate, user });\n\t\t\tvoid afterTakeInquiry({ inquiry: returnedInquiry, room: roomAfterUpdate, agent });\n\n\t\t\tvoid notifyOnLivechatInquiryChangedById(inquiry._id, 'updated', {\n\t\t\t\tstatus: LivechatInquiryStatus.TAKEN,","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/omnichannel/RoutingManager.ts#L290-L326","documentation":"Livechat inquiries are taken with optimistic concurrency: LivechatInquiry.takeInquiry(_id, lockedAt) only updates the document when lockedAt still matches the value that was read earlier. When modifiedCount is 0, the inquiry was concurrently locked, taken, or rewritten by someone else between the read and the write, and RoutingManager throws Error('error-taking-inquiry-lockedAt-mismatch') instead of double-assigning the chat.","triggerScenarios":"Two actors (two agents, or an agent plus auto-routing) read the same queued inquiry and both attempt to take it: the first update wins, the second sees modifiedCount 0 and gets this error. Also fired when a department forward or routing callback rewrites the inquiry between the read and the take.","commonSituations":"Busy queues where several agents click the same inquiry at once; auto-assignment racing with manual takes; duplicated websocket events re-triggering the take for the same inquiry.","solutions":["Re-fetch the inquiry and retry the take only if it is still queued - someone else most likely won the race","Refresh the queue view so already-taken inquiries disappear from agent lists","Ensure only one assignment path (auto-routing OR manual take) can act on an inquiry at a time","Surface a friendly 'chat already taken' outcome instead of treating it as a failure"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await RoutingManager.takeInquiry(_id, options);\n} catch (err: any) {\n  if (err?.message === 'error-taking-inquiry-lockedAt-mismatch') {\n    const fresh = await LivechatInquiry.findOneById(_id);\n    if (fresh && !fresh.servedBy) {\n      return RoutingManager.takeInquiry(_id, options); // someone rewrote it; retry once with fresh lockedAt\n    }\n    // lost the race: refresh the queue view and move on\n  }\n}","preventionTips":["Keep the inquiry queue view near-real-time so agents stop clicking already-taken chats","Never run auto-assignment and manual take against the same queue simultaneously","Treat this as an expected race outcome ('chat already taken'), not an incident"],"tags":["omnichannel","routing","concurrency","optimistic-locking","race-condition"],"backgroundTag":"optimistic-locking-conflict","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}