{"record":{"id":"87880360f8b2acc2","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-room-878803","errorCode":"error-invalid-room","errorMessage":"Invalid room","messagePattern":"Invalid room","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/messages/loadMissedMessages.ts","lineNumber":26,"sourceCode":"import { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';\n\ndeclare module '@rocket.chat/ddp-client' {\n\t// eslint-disable-next-line @typescript-eslint/naming-convention\n\tinterface ServerMethods {\n\t\tloadMissedMessages(rid: IRoom['_id'], ts: Date): Promise<false | IMessage[]>;\n\t}\n}\n\nMeteor.methods<ServerMethods>({\n\tasync loadMissedMessages(rid, start) {\n\t\tmethodDeprecationLogger.method('loadMissedMessages', '9.0.0', '/v1/chat.syncMessages');\n\t\tcheck(rid, String);\n\t\tcheck(start, Date);\n\n\t\tconst fromId = Meteor.userId() ?? undefined;\n\n\t\tif (!rid) {\n\t\t\tthrow new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getUsersOfRoom' });\n\t\t}\n\n\t\tif (!(await canAccessRoomIdAsync(rid, fromId))) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn Messages.findVisibleByRoomIdAfterTimestamp(rid, start, true, {\n\t\t\tsort: {\n\t\t\t\tts: -1,\n\t\t\t},\n\t\t}).toArray();\n\t},\n});\n","sourceCodeStart":8,"sourceCodeEnd":40,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/messages/loadMissedMessages.ts#L8-L40","documentation":"loadMissedMessages throws error-invalid-room when rid is falsy. Because check(rid, String) runs first and enforces the type, the only value that reaches this throw is the empty string '' (null/undefined/number fail earlier with a Match failed error instead). Two quirks: the error's method metadata says 'getUsersOfRoom' — a copy-paste artifact — and the method is deprecated since 9.0.0 in favor of /v1/chat.syncMessages. Note that failing canAccessRoomIdAsync does NOT throw here; it returns false.","triggerScenarios":"Meteor.call('loadMissedMessages', '', startDate) — the rid comes from an unset variable, an empty route param, or a room record that has not loaded yet when the sync fires.","commonSituations":"Races where loadMissedMessages fires before the room subscription delivers the rid; refactors that renamed variables and accidentally pass ''; optional :rid route params rendered without validation.","solutions":["Guard rid before calling — only call when it is a non-empty string","Fix the source of the empty rid (await the room record, validate route params)","Migrate to /v1/chat.syncMessages, which validates its own input"],"exampleFix":"// before\nconst messages = await Meteor.callAsync('loadMissedMessages', rid, lastSync);\n\n// after\nif (typeof rid !== 'string' || rid.length === 0) {\n  // wait for the room id instead of calling with ''\n  return;\n}\nconst messages = await Meteor.callAsync('loadMissedMessages', rid, lastSync);","handlingStrategy":"validation","validationCode":"if (typeof rid !== 'string' || rid.length === 0) {\n  // wait for the room record — never call loadMissedMessages with ''\n}","typeGuard":"const isNonEmptyRoomId = (rid: unknown): rid is string =>\n  typeof rid === 'string' && rid.trim().length > 0;","tryCatchPattern":null,"preventionTips":["Derive rid from a loaded room record or subscription, never from an unvalidated variable","Remember check() already rejects non-strings with Match failed — this error means the empty string specifically","Prefer /v1/chat.syncMessages; the DDP method is deprecated since 9.0.0 and its error metadata mislabels the method"],"tags":["validation","room-id","meteor-method","legacy"],"backgroundTag":"invalid-room-id","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}