{"record":{"id":"e15c6deac2ebd772","repo":"RocketChat/Rocket.Chat","slug":"error-room-does-not-exist-e15c6d","errorCode":"error-room-does-not-exist","errorMessage":"This room does not exist","messagePattern":"This room does not exist","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/api/v1/chat.ts","lineNumber":298,"sourceCode":"\t\tasync function action() {\n\t\t\tif (!settings.get<boolean>('Threads_enabled')) {\n\t\t\t\tthrow new Meteor.Error('error-not-allowed', 'Threads Disabled');\n\t\t\t}\n\n\t\t\tconst { tmid } = this.bodyParams;\n\n\t\t\tconst thread = await Messages.findOneById(tmid, { projection: { rid: 1 } });\n\t\t\tif (!thread?.rid) {\n\t\t\t\tthrow new Meteor.Error('error-invalid-message', 'Invalid Message');\n\t\t\t}\n\n\t\t\tconst [user, room] = await Promise.all([\n\t\t\t\tUsers.findOneById(this.userId),\n\t\t\t\tRooms.findOneById(thread.rid, { projection: { ...roomAccessAttributes, t: 1, _id: 1 } }),\n\t\t\t]);\n\n\t\t\tif (!room) {\n\t\t\t\tthrow new Meteor.Error('error-room-does-not-exist', 'This room does not exist');\n\t\t\t}\n\n\t\t\tif (!user || !(await canAccessRoomAsync(room, user))) {\n\t\t\t\tthrow new Meteor.Error('error-not-allowed', 'Not Allowed');\n\t\t\t}\n\n\t\t\tawait callbacks.run('beforeReadMessages', room._id, user._id);\n\t\t\tawait readThread({ user, room, tmid });\n\n\t\t\treturn API.v1.success();\n\t\t},\n\t)\n\t.post(\n\t\t'chat.update',\n\t\t{\n\t\t\tauthRequired: true,\n\t\t\tbody: isChatUpdateProps,\n\t\t\tresponse: {","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/server/api/v1/chat.ts#L280-L316","documentation":"Thrown by chat.readThread when Rooms.findOneById(thread.rid, ...) returns null - the room referenced by the thread parent message no longer exists in the Rooms collection. Meteor.Error code 'error-room-does-not-exist'. This is a data-integrity edge: the message exists but its room is gone.","triggerScenarios":"The thread root message exists but its room was deleted (orphan message); rid on the message points to a room id that was purged.","commonSituations":"Room deletion that did not clean up thread messages; data migration that dropped rooms but kept messages; replica lag in a sharded setup.","solutions":["Treat error-room-does-not-exist as a signal to drop the thread from the client (it is orphaned).","Run a data cleanup to remove orphaned messages whose rid no longer resolves.","Investigate room-deletion hooks if orphans recur (beforeDeleteRoom should cascade to messages)."],"exampleFix":"// before\ntry {\n  await api.readThread(tmid);\n} catch (e) {\n  showError(e);\n}\n\n// after - gracefully drop orphaned threads\ntry {\n  await api.readThread(tmid);\n} catch (e) {\n  if (e.error === 'error-room-does-not-exist') {\n    removeThreadFromUI(tmid);\n    return;\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"async function threadRoomExists(tmid) {\n  const msg = await Messages.findOneById(tmid, { projection: { rid: 1 } });\n  if (!msg?.rid) return false;\n  const room = await Rooms.findOneById(msg.rid, { projection: { _id: 1 } });\n  return Boolean(room);\n}","typeGuard":"function hasResolvableRoom(msg, room) {\n  return Boolean(msg && msg.rid && room && room._id === msg.rid);\n}","tryCatchPattern":"try {\n  await api.readThread({ tmid });\n} catch (e) {\n  if (e.error === 'error-room-does-not-exist') {\n    removeOrphanedThread(tmid); // room gone, drop the thread\n    return;\n  }\n  throw e;\n}","preventionTips":["Drop orphaned threads (room deleted) from the client on this error.","Run data cleanup to remove messages whose rid no longer resolves.","Ensure beforeDeleteRoom cascades to messages to prevent orphans."],"tags":["chat","threads","room-not-found","data-integrity"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}