{"record":{"id":"93c793a750ceaa37","repo":"RocketChat/Rocket.Chat","slug":"error-user-not-banned","errorCode":"error-user-not-banned","errorMessage":"error-user-not-banned","messagePattern":"error-user-not-banned","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/rooms/executeUnbanUserFromRoom.ts","lineNumber":36,"sourceCode":"\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\n\t// if the subscription exists and is not an invite and not banned\n\tif (!isBannedSubscription(subscription)) {\n\t\tthrow new Error('error-user-not-banned');\n\t}\n\n\t// Remove the subscription entirely — the user is no longer banned but also not a member.\n\t// Room count and __rooms were already adjusted during ban, so we only delete the document.\n\tawait Subscriptions.removeById(subscription._id);\n\n\tawait Message.saveSystemMessage('user-unbanned', rid, user.username, user, {\n\t\tu: { _id: byUser._id, username: byUser.username },\n\t});\n\n\tvoid notifyOnSubscriptionChanged(subscription, 'removed');\n\tvoid notifyOnRoomChangedById(rid);\n\n\tconst inviterUser = await Users.findOneById(byUser._id);\n\tif (inviterUser) {\n\t\tawait afterUnbanFromRoomCallback.run({ unbannedUser: user, userWhoUnbanned: inviterUser }, room);\n\t}\n};","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/rooms/executeUnbanUserFromRoom.ts#L18-L54","documentation":"After finding the subscription, executeUnbanUserFromRoom checks isBannedSubscription(subscription). If the subscription exists but is neither an invite nor banned (ls/open like a normal member), unban makes no sense and the flow throws Error 'error-user-not-banned'.","triggerScenarios":"Unbanning a user who is an active member (subscription without ban status), or whose ban marker was cleared by a concurrent unban. The isInviteSubscription branch handled earlier returns without error; only non-invite, non-banned subscriptions reach this throw.","commonSituations":"Stale moderation UI listing a user as banned after another moderator already unbanned them; permission misconfiguration letting unauthenticated flows call unban; events duplicated by federation or apps.","solutions":["Pre-check isBannedSubscription before unbanning; skip when the user is not banned","Refresh moderation state after concurrent unbans so stale UIs do not resubmit","Serialize ban/unban moderation actions per (rid, userId) to avoid races"],"exampleFix":"// before\nMeteor.call('unbanUserFromRoom', rid, userId); // may throw error-user-not-banned\n\n// after\nconst sub = await Subscriptions.findOneByRoomIdAndUserId(rid, userId);\nif (!sub || (!isBannedSubscription(sub) && !isInviteSubscription(sub))) return;\nMeteor.call('unbanUserFromRoom', rid, userId);","handlingStrategy":"validation","validationCode":"const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id);\nif (sub && !isInviteSubscription(sub) && !isBannedSubscription(sub)) {\n  // user is a normal active member — nothing to unban\n  return;\n}\nawait executeUnbanUserFromRoom(rid, user, byUser);","typeGuard":"import { isBannedSubscription } from '@rocket.chat/core-typings';\n// subscription has banned/anchor state -> unban is meaningful","tryCatchPattern":"try {\n  await executeUnbanUserFromRoom(rid, user, byUser);\n} catch (err) {\n  if (err instanceof Error && err.message === 'error-user-not-banned') {\n    // stale UI state — refresh the banned user list\n  }\n  throw err;\n}","preventionTips":["Refresh banned-user lists from the subscription state before rendering actions","Treat error-user-not-banned as idempotent success in federation event handlers","Lock concurrent moderation ops per (rid, userId) to avoid races"],"tags":["unban","subscription","state-conflict"],"backgroundTag":"stale-subscription-state","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}