{"record":{"id":"dff628fb96bd8328","repo":"RocketChat/Rocket.Chat","slug":"error-not-invited","errorCode":"error-not-invited","errorMessage":"User was not invited to this room ${subscription.status}","messagePattern":"User was not invited to this room (.+?)","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/rooms/acceptRoomInvite.ts","lineNumber":26,"sourceCode":"import { callbacks } from '../callbacks';\nimport { notifyOnSubscriptionChangedById } from '../notifyListener';\n\n/**\n * Accepts a room invite when triggered by internal events such as federation\n * or third-party callbacks. Performs the necessary database updates and triggers\n * safe callbacks, ensuring no propagation loops are created during external event\n * processing.\n */\n\n// TODO this funcion is pretty much the same as the one in addUserToRoom.ts, we should probably\n// unify them at some point\nexport const performAcceptRoomInvite = async (\n\troom: IRoom,\n\tsubscription: ISubscription,\n\tuser: IUser & { username: string },\n): Promise<void> => {\n\tif (subscription.status !== 'INVITED' || !subscription.inviter) {\n\t\tthrow new Meteor.Error('error-not-invited', `User was not invited to this room ${subscription.status}`);\n\t}\n\tconst inviter = await Users.findOneById(subscription.inviter._id);\n\n\tawait callbacks.run('beforeJoinRoom', user, room);\n\n\tawait callbacks.run('beforeAddedToRoom', { user, inviter }, room);\n\n\ttry {\n\t\tawait Apps.self?.triggerEvent(AppEvents.IPreRoomUserJoined, room, user, inviter);\n\t} catch (error: any) {\n\t\tif (error.name === AppsEngineException.name) {\n\t\t\tthrow new Meteor.Error('error-app-prevented', error.message);\n\t\t}\n\n\t\tthrow error;\n\t}\n\n\tawait Subscriptions.acceptInvitationById(subscription._id);","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/rooms/acceptRoomInvite.ts#L8-L44","documentation":"Meteor.Error('error-not-invited') thrown by performAcceptRoomInvite when subscription.status !== 'INVITED' or subscription.inviter is falsy. The accept flow only works on a still-pending invitation; anything else is rejected before the join callbacks run.","triggerScenarios":"Calling acceptRoomInvite for an invitation that was already accepted or declined, whose subscription was superseded (kick/re-invite), or whose inviter metadata is missing.","commonSituations":"User double-clicks Accept; two tabs accept the same invite; stale invite links/emails; imported subscriptions without inviter data.","solutions":["Refetch the subscription and only offer 'accept' while status === 'INVITED' and inviter exists.","Treat the error as idempotency feedback: refresh the room list and continue without alarming the user.","Repair subscriptions missing inviter metadata if produced by an import."],"exampleFix":"// before\nawait performAcceptRoomInvite(room, subscription, user); // throws error-not-invited\n\n// after\nif (subscription.status === 'INVITED' && subscription.inviter) {\n  await performAcceptRoomInvite(room, subscription, user);\n}","handlingStrategy":"validation","validationCode":"const sub = await Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, { projection: { status: 1, inviter: 1 } });\nif (sub?.status === 'INVITED' && sub.inviter) {\n  await performAcceptRoomInvite(room, sub, user as IUser & { username: string });\n} else {\n  // not a pending invite: refresh state instead of accepting\n}","typeGuard":"const isPendingInvite = (\n  s: ISubscription | null | undefined,\n): s is ISubscription & { status: 'INVITED'; inviter: NonNullable<ISubscription['inviter']> } =>\n  !!s && s.status === 'INVITED' && !!s.inviter;","tryCatchPattern":"try {\n  await performAcceptRoomInvite(room, subscription, user);\n} catch (error: any) {\n  if (error?.error === 'error-not-invited') {\n    // already accepted/declined: refresh the room list and continue\n  }\n}","preventionTips":["Make accept idempotent in the UI: disable the button after the first click.","Refetch the subscription before accepting.","Treat 'error-not-invited' as 'already handled', not as an error for the user."],"tags":["invitations","rooms","subscriptions","idempotency"],"backgroundTag":"invalid-invitation-state","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}