{"record":{"id":"90fc9696239eb543","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-user-90fc96","errorCode":"error-invalid-user","errorMessage":"Invalid user","messagePattern":"Invalid user","errorType":"error_code","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/messages/deleteMessage.ts","lineNumber":18,"sourceCode":"import { AppEvents, Apps } from '@rocket.chat/apps';\nimport { api, Message } from '@rocket.chat/core-services';\nimport { isThreadMessage, type AtLeast, type IMessage, type IRoom, type IThreadMessage, type IUser } from '@rocket.chat/core-typings';\nimport { Messages, Rooms, Uploads, Users, ReadReceipts, ReadReceiptsArchive, Subscriptions } from '@rocket.chat/models';\nimport { Meteor } from 'meteor/meteor';\n\nimport { settings } from '../../settings';\nimport { canDeleteMessageAsync } from '../authorization/canDeleteMessage';\nimport { callbacks } from '../callbacks';\nimport { FileUpload } from '../media/file-upload';\nimport { notifyOnRoomChangedById, notifyOnMessageChange, notifyOnSubscriptionChangedByRoomIdAndUserIds } from '../notifyListener';\n\nexport const deleteMessageValidatingPermission = async (message: AtLeast<IMessage, '_id'>, userId: IUser['_id']): Promise<void> => {\n\tif (!message?._id) {\n\t\tthrow new Meteor.Error('error-invalid-message', 'Invalid message');\n\t}\n\tif (!userId) {\n\t\tthrow new Meteor.Error('error-invalid-user', 'Invalid user');\n\t}\n\n\tconst user = await Users.findOneById(userId);\n\tconst originalMessage = await Messages.findOneById(message._id);\n\n\tif (!originalMessage || !user || !(await canDeleteMessageAsync(user, originalMessage))) {\n\t\tthrow new Meteor.Error('error-action-not-allowed', 'Not allowed');\n\t}\n\n\treturn deleteMessage(originalMessage, user);\n};\n\nexport async function deleteMessage(message: IMessage, user: IUser): Promise<void> {\n\tconst deletedMsg: IMessage | null = await Messages.findOneById(message._id);\n\tconst isThread = (deletedMsg?.tcount || 0) > 0;\n\tconst keepHistory = settings.get('Message_KeepHistory') || isThread;\n\tconst showDeletedStatus = settings.get('Message_ShowDeletedStatus') || isThread;\n","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/messages/deleteMessage.ts#L1-L36","documentation":"deleteMessageValidatingPermission (deleteMessage.ts:17-19) requires a truthy userId before performing a delete. Without a user id there is no principal to authorize, so the call fails fast with error-invalid-user 'Invalid user' - before any DB lookup happens.","triggerScenarios":"Invoking the deleteMessage method from an unauthenticated DDP connection; server code calling the helper with undefined userId (e.g. a background job that lost its user context); REST wrappers that drop the authenticated user when forwarding the call.","commonSituations":"Expired login sessions where the client still queues the delete; custom server modules calling deletion logic outside a request scope; tests that forget to stub the user; federation/app bridges that must pick a user to act as.","solutions":["Ensure the call runs inside an authenticated session and forward that userId (this.userId in a Meteor method)","For server-side jobs, resolve the acting user first (e.g. the app/bot owner) and pass its _id explicitly","Re-authenticate the client when the session expired, then retry the delete","Guard the call site: skip deletion when userId is falsy instead of throwing"],"exampleFix":"// before\ndeleteMessageValidatingPermission(message, undefined); // -> error-invalid-user\n// after\nconst userId = this.userId ?? (await resolveBotUserId());\nif (userId) await deleteMessageValidatingPermission(message, userId);","handlingStrategy":"validation","validationCode":"if (!userId) throw new Error('Deleting requires an authenticated user');\nawait deleteMessageValidatingPermission(message, userId);","typeGuard":null,"tryCatchPattern":"try {\n  await deleteMessageValidatingPermission(message, userId);\n} catch (error: any) {\n  if (error instanceof Meteor.Error && error.error === 'error-invalid-user') {\n    await reauthenticate(); // then retry once with a fresh session\n    return;\n  }\n  throw error;\n}","preventionTips":["Bind deletions to this.userId inside Meteor methods","Skip delete actions when the session is anonymous","Check login state before enabling delete UI affordances"],"tags":["messages","delete","authentication","input-validation"],"backgroundTag":"unauthenticated-request","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}