{"record":{"id":"b344201cb38b1338","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-user-b34420","errorCode":"error-invalid-user","errorMessage":"error-invalid-user","messagePattern":"error-invalid-user","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/rooms/executeUnbanUserFromRoom.ts","lineNumber":15,"sourceCode":"import { Message } from '@rocket.chat/core-services';\nimport { isBannedSubscription, isInviteSubscription, type IUser } from '@rocket.chat/core-typings';\nimport { Rooms, Subscriptions, Users } from '@rocket.chat/models';\n\nimport { afterUnbanFromRoomCallback } from '../callbacks/afterUnbanFromRoomCallback';\nimport { notifyOnRoomChangedById, notifyOnSubscriptionChanged } from '../notifyListener';\n\nexport const executeUnbanUserFromRoom = async function (rid: string, user: IUser, byUser: IUser): Promise<void> {\n\tconst room = await Rooms.findOneById(rid);\n\tif (!room) {\n\t\tthrow new Error('error-invalid-room');\n\t}\n\n\tif (!user.username) {\n\t\tthrow new Error('error-invalid-user');\n\t}\n\n\tconst subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id);\n\tif (!subscription) {\n\t\tthrow new Error('error-invalid-subscription');\n\t}\n\n\t// if the subscription is an invite it means we were unbanned and then invited again, then\n\t// the invite was accepted and we receive a leave event (meaning the user was unbanned), so\n\t// at this point we just need send the message to say the user was unbanned.\n\tif (isInviteSubscription(subscription)) {\n\t\tawait Message.saveSystemMessage('user-unbanned', rid, user.username, user, {\n\t\t\tu: { _id: byUser._id, username: byUser.username },\n\t\t});\n\n\t\treturn;\n\t}\n","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/rooms/executeUnbanUserFromRoom.ts#L1-L33","documentation":"executeUnbanUserFromRoom requires the target user to have a username because the unban flow writes a 'user-unbanned' system message attributed to that username. A user object without username (undefined) triggers Error 'error-invalid-user'.","triggerScenarios":"Passing a user document fetched with a projection that omits username, or a user record that genuinely has no username (custom auth not yet finalized), into the unban flow.","commonSituations":"Federation/app code passing partial user objects; user records mid-provisioning; queries using projections like { _id: 1 } reused across features.","solutions":["Fetch the full user document (no projection) before the unban call","Ensure the target user has a username (finish custom-auth provisioning) before moderation actions","Type calling-code parameters as Pick<IUser, '_id' | 'username'>"],"exampleFix":"// before\nawait executeUnbanUserFromRoom(rid, user, byUser);\n\n// after\nif (!user.username) {\n  throw new Meteor.Error('error-invalid-user', 'Target user has no username');\n}\nawait executeUnbanUserFromRoom(rid, user, byUser);","handlingStrategy":"type-guard","validationCode":"if (!user.username) {\n  throw new Meteor.Error('error-invalid-user', 'Target user has no username');\n}\nawait executeUnbanUserFromRoom(rid, user, byUser);","typeGuard":"const hasUsername = (user: Pick<IUser, '_id'> | IUser): user is IUser & { username: string } =>\n  Boolean(user.username);","tryCatchPattern":"try {\n  await executeUnbanUserFromRoom(rid, user, byUser);\n} catch (err) {\n  if (err instanceof Error && err.message === 'error-invalid-user') {\n    // re-fetch the full user document and retry once\n  }\n  throw err;\n}","preventionTips":["Fetch full user documents for moderation flows","Guard username before any moderation method call","Add a DB invariant/test asserting users always have usernames post-provisioning"],"tags":["unban","validation","username-missing"],"backgroundTag":"incomplete-user-record","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}