RocketChat/Rocket.Chat · error

error-taking-inquiry-lockedAt-mismatch

error-taking-inquiry-lockedAt-mismatch

Error message

error-taking-inquiry-lockedAt-mismatch

What it means

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.

Source

Thrown at apps/meteor/server/lib/omnichannel/RoutingManager.ts:308

				}
				agent = null;
			}
		}

		if (!agent) {
			logger.debug({ msg: 'Cannot take inquiry. Precondition failed for agent', inquiryId: inquiry._id });
			const cbRoom = await callbacks.run<'livechat.onAgentAssignmentFailed'>('livechat.onAgentAssignmentFailed', room, {
				inquiry,
				options,
			});
			return cbRoom;
		}

		try {
			const result = await LivechatInquiry.takeInquiry(_id, inquiry.lockedAt);
			if (result.modifiedCount === 0) {
				logger.error({ msg: 'Failed to take inquiry because lockedAt did not match', inquiryId: _id, lockedAt: inquiry.lockedAt });
				throw new Error('error-taking-inquiry-lockedAt-mismatch');
			}

			logger.info({ msg: 'Inquiry taken', inquiryId: _id, agentId: agent.agentId });

			// assignAgent changes the room data to add the agent serving the conversation. afterTakeInquiry expects room object to be updated
			const { inquiry: returnedInquiry, user } = await this.assignAgent(inquiry, agent);
			const roomAfterUpdate = await LivechatRooms.findOneById(rid);

			if (!roomAfterUpdate) {
				// This should never happen
				throw new Error('error-room-not-found');
			}

			void Apps.self?.triggerEvent(AppEvents.IPostLivechatAgentAssigned, { room: roomAfterUpdate, user });
			void afterTakeInquiry({ inquiry: returnedInquiry, room: roomAfterUpdate, agent });

			void notifyOnLivechatInquiryChangedById(inquiry._id, 'updated', {
				status: LivechatInquiryStatus.TAKEN,

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Re-fetch the inquiry and retry the take only if it is still queued - someone else most likely won the race
  2. Refresh the queue view so already-taken inquiries disappear from agent lists
  3. Ensure only one assignment path (auto-routing OR manual take) can act on an inquiry at a time
  4. Surface a friendly 'chat already taken' outcome instead of treating it as a failure
Defensive patterns

Strategy: retry

Try / catch

try {
  await RoutingManager.takeInquiry(_id, options);
} catch (err: any) {
  if (err?.message === 'error-taking-inquiry-lockedAt-mismatch') {
    const fresh = await LivechatInquiry.findOneById(_id);
    if (fresh && !fresh.servedBy) {
      return RoutingManager.takeInquiry(_id, options); // someone rewrote it; retry once with fresh lockedAt
    }
    // lost the race: refresh the queue view and move on
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/6a7de50f6b8db40a. Report an issue: GitHub.