{"record":{"id":"417dc81094964d87","repo":"RocketChat/Rocket.Chat","slug":"error-room-cannot-be-closed-try-again","errorCode":"error-room-cannot-be-closed-try-again","errorMessage":"error-room-cannot-be-closed-try-again","messagePattern":"error-room-cannot-be-closed-try-again","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/omnichannel/closeRoom.ts","lineNumber":55,"sourceCode":"\t\tconst { room, closedBy, removedInquiry } = await doCloseRoom(params, session);\n\t\tawait session.commitTransaction();\n\n\t\tnewRoom = room;\n\t\tchatCloser = closedBy;\n\t\tremovedInquiryObj = removedInquiry;\n\t} catch (e) {\n\t\tlogger.error({ err: e, msg: 'Failed to close room', afterAttempts: attempts });\n\t\tif (session.inTransaction()) {\n\t\t\tawait session.abortTransaction();\n\t\t}\n\t\t// Dont propagate transaction errors\n\t\tif (shouldRetryTransaction(e)) {\n\t\t\tif (attempts > 0) {\n\t\t\t\tlogger.debug({ msg: 'Retrying close room because of transient error', attemptsLeft: attempts });\n\t\t\t\treturn closeRoom(params, attempts - 1);\n\t\t\t}\n\n\t\t\tthrow new Error('error-room-cannot-be-closed-try-again');\n\t\t}\n\t\tthrow e;\n\t} finally {\n\t\tawait session.endSession();\n\t}\n\n\t// Note: when reaching this point, the room has been closed\n\t// Transaction is commited and so these messages can be sent here.\n\treturn afterRoomClosed(newRoom, chatCloser, removedInquiryObj, params);\n}\n\nasync function afterRoomClosed(\n\tnewRoom: IOmnichannelRoom,\n\tchatCloser: ChatCloser,\n\tinquiry: ILivechatInquiryRecord | null,\n\tparams: CloseRoomParams,\n): Promise<void> {\n\tif (!chatCloser) {","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/omnichannel/closeRoom.ts#L37-L73","documentation":"closeRoom performs its closing writes inside a MongoDB transaction with a bounded retry loop for transient errors (shouldRetryTransaction). When the retries are exhausted on a transient error (write conflict, primary stepdown, network blip), it aborts the session and throws Error('error-room-cannot-be-closed-try-again'). The room is left open and consistent - the transaction rolled back cleanly.","triggerScenarios":"Heavy concurrent writes to the same room/inquiry documents (parallel closes, message bursts) repeatedly causing TransientTransactionError/UnknownTransactionCommitResult; a MongoDB replica set failover or primary stepdown mid-close; a degraded or undersized replica set under load.","commonSituations":"Bulk-closing many chats at once (e.g. closeOpenChats for agents with huge loads); multi-instance deployments fanning out closes; Mongo under backup/migration pressure; replica sets with lagging secondaries or frequent elections.","solutions":["Retry the close after a short backoff - the error message explicitly says try again","Check MongoDB health: replica set status, primary stability, replication lag","Stagger bulk-close batches instead of firing all closes concurrently","If persistent, inspect logs for the underlying Mongo error (write conflicts, NoSuchTransaction) and resolve the contention source"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"const closeWithBackoff = async (params: CloseRoomParams, attempts = 4): Promise<void> => {\n  try {\n    await closeRoom(params);\n  } catch (err: any) {\n    if (err?.message === 'error-room-cannot-be-closed-try-again' && attempts > 0) {\n      await sleep(2 ** (4 - attempts) * 250); // 250ms, 500ms, 1s, 2s\n      return closeWithBackoff(params, attempts - 1);\n    }\n    throw err;\n  }\n};","preventionTips":["Run MongoDB as a healthy replica set - transactions fail noisily during failovers and stepdowns","Stagger bulk-close operations to avoid write-conflict storms on shared room/inquiry documents","Alert on repeated 'try-again' failures: sustained occurrence means Mongo contention or instability, not bad input"],"tags":["omnichannel","mongodb","transactions","transient","retry"],"backgroundTag":"transient-transaction-failure","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"}