toeverything/AFFiNE · error · InvalidInvitation
invalid_invitation
invalid_invitation
Error message
Invalid invitation provided.
What it means
Thrown by `acceptInviteById` on the email-invitation branch when an invitation row exists for `inviteId`, the caller is signed in, but `user.id !== role.userId` — i.e. a different authenticated account is trying to accept an invitation emailed to someone else. Classified as `invalid_invitation` (invalid_input).
Source
Thrown at packages/backend/server/src/core/workspaces/resolvers/member.ts:704
@Mutation(() => Boolean)
@Public()
async acceptInviteById(
@CurrentUser() user: CurrentUser | undefined,
@Args('inviteId') inviteId: string,
@Args('workspaceId', { deprecationReason: 'never used', nullable: true })
_workspaceId: string,
@Args('sendAcceptMail', {
nullable: true,
deprecationReason: 'never used',
})
_sendAcceptMail: boolean
) {
const role = await this.models.workspaceUser.getById(inviteId);
// invitation by email
if (role) {
if (user && user.id !== role.userId) {
throw new InvalidInvitation();
}
await this.acceptInvitationByEmail(role);
} else {
// invitation by link
if (!user) {
throw new AuthenticationRequired();
}
const invitation = await this.cache.get<{
workspaceId: string;
inviterUserId: string;
}>(`workspace:inviteLinkId:${inviteId}`);
if (!invitation) {
throw new InvalidInvitation();
}
View on GitHub (pinned to 26c515e050)
Solutions
- Sign out and accept the invite in an incognito window, then sign in with the exact email the invite was sent to.
- Have an admin re-send the invitation to the account the user is actually signed in with.
- In the client, compare the invited email to the current session email before calling `acceptInviteById`.
- If the invite is meant to be transferable, use a link invitation instead of an email invitation.
Example fix
// before
await sdk.acceptInviteById({ inviteId });
// after
const info = await sdk.getInviteInfo({ inviteId });
if (currentUser && info.inviteeUserId && currentUser.id !== info.inviteeUserId) {
promptSignOutAndRedirect(inviteId);
return;
}
await sdk.acceptInviteById({ inviteId }); Defensive patterns
Strategy: validation
Validate before calling
// Compare current session to the invited user before accepting
const info = await sdk.getInviteInfo({ inviteId });
if (currentUser && info.inviteeUserId && currentUser.id !== info.inviteeUserId) {
promptSignOut(inviteId);
return;
}
await sdk.acceptInviteById({ inviteId }); Type guard
function inviteMatchesSession(currentUser, inviteInfo) {
return !currentUser || !inviteInfo.inviteeUserId || currentUser.id === inviteInfo.inviteeUserId;
} Try / catch
try {
await sdk.acceptInviteById({ inviteId });
} catch (e) {
if (e.code === 'invalid_invitation') {
notify('Sign in with the email that received this invite, or request a new one.');
} else throw e;
} Prevention
- Accept email invites in a session matching the invited email.
- Compare session user id to the invitee before accepting.
- Use link invites if the invite should be transferable between users.
When it happens
Trigger: Clicking an invite link while signed in as account B when the invite was emailed to account A; sharing an email-invite id with a colleague who is logged into their own account; a session that was switched mid-flow.
Common situations: User received the invite at work email but is signed in with a personal account; browser auto-filled a different account; testing invites by forwarding the link to another user who is already authenticated.
Related errors
- authentication_required
- can_not_revoke_yourself
- owner_can_not_leave_workspace
- space_access_denied
- space_not_found
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/24879a9b525b2e62.
Report an issue: GitHub.