{"record":{"id":"4d8cc103df794307","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-user-4d8cc1","errorCode":"error-invalid-user","errorMessage":"Invalid user","messagePattern":"Invalid user","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/messages/loadNextMessages.ts","lineNumber":23,"sourceCode":"import { Meteor } from 'meteor/meteor';\n\nimport { canAccessRoomIdAsync } from '../../lib/authorization/canAccessRoom';\nimport { normalizeMessagesForUser } from '../../lib/utils/lib/normalizeMessagesForUser';\n\ndeclare module '@rocket.chat/ddp-client' {\n\t// eslint-disable-next-line @typescript-eslint/naming-convention\n\tinterface ServerMethods {\n\t\tloadNextMessages(rid: IRoom['_id'], end?: Date, limit?: number): Promise<{ messages: IMessage[] }>;\n\t}\n}\n\nMeteor.methods<ServerMethods>({\n\tasync loadNextMessages(rid, end, limit = 20) {\n\t\tcheck(rid, String);\n\t\tcheck(limit, Number);\n\n\t\tif (!Meteor.userId()) {\n\t\t\tthrow new Meteor.Error('error-invalid-user', 'Invalid user', {\n\t\t\t\tmethod: 'loadNextMessages',\n\t\t\t});\n\t\t}\n\n\t\tif (!rid) {\n\t\t\tthrow new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'loadNextMessages' });\n\t\t}\n\n\t\tconst fromId = Meteor.userId();\n\n\t\tif (!fromId || !(await canAccessRoomIdAsync(rid, fromId))) {\n\t\t\tthrow new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'loadNextMessages' });\n\t\t}\n\n\t\tlet records;\n\t\tif (end) {\n\t\t\trecords = await Messages.findVisibleByRoomIdAfterTimestamp(rid, end, true, {\n\t\t\t\tsort: {","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/messages/loadNextMessages.ts#L5-L41","documentation":"loadNextMessages requires an authenticated caller: Meteor.userId() returning null throws error-invalid-user before rid or room access is evaluated. The method fetches messages following a given timestamp and has no anonymous mode (unlike loadHistory).","triggerScenarios":"Meteor.call('loadNextMessages', rid, end, limit) from a logged-out tab, with an invalidated resume token, or from a DDP client that never performed a login.","commonSituations":"Expired sessions in long-lived tabs; 'load more' buttons firing after logout; automation scripts calling the method without authenticating.","solutions":["Check Meteor.userId() before calling and gate the UI on login state","Re-authenticate on session expiry, then retry the fetch","For scripts, log in over DDP before invoking the method"],"exampleFix":"// before\nconst { messages } = await Meteor.callAsync('loadNextMessages', rid, end, limit);\n\n// after\nif (!Meteor.userId()) {\n  // require login before paging newer messages\n} else {\n  const { messages } = await Meteor.callAsync('loadNextMessages', rid, end, limit);\n}","handlingStrategy":"validation","validationCode":"if (!Meteor.userId()) {\n  // loadNextMessages requires a logged-in user\n}","typeGuard":null,"tryCatchPattern":"try {\n  const { messages } = await Meteor.callAsync('loadNextMessages', rid, end, limit);\n} catch (error) {\n  if (error instanceof Meteor.Error && error.error === 'error-invalid-user') {\n    // session expired — re-authenticate and retry once\n  } else {\n    throw error;\n  }\n}","preventionTips":["Disable 'load newer messages' controls in logged-out states","Cancel in-flight paging calls on logout","For DDP scripts, complete login before calling"],"tags":["authentication","meteor-method","ddp","session"],"backgroundTag":"not-authenticated","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}