toeverything/AFFiNE · error · Error
Cannot activate a missing workspace invitation.
Error message
Cannot activate a missing workspace invitation.
What it means
Thrown by WorkspaceInvitationModel.setState (permission-write.ts:313) when asked to move a user to a non-invitation (Accepted) state but no invitation row exists in workspace_invitations for that user. Without the invitation the model cannot derive the requested role (admin vs member) needed to activate the member, so it refuses. Plain Error.
Source
Thrown at packages/backend/server/src/models/permission-write.ts:313
status: invitationStatus,
kind: workspaceInvitationKindToNew(data.source),
});
}
@Transactional()
async setState(
workspaceId: string,
userId: string,
status: WorkspaceMemberStatus,
data: {
inviterId?: string;
} = {}
) {
const invitationStatus = workspaceStatusToInvitationState(status);
if (!invitationStatus) {
const invitation = await this.findInvitation(workspaceId, userId);
if (!invitation) {
throw new Error('Cannot activate a missing workspace invitation.');
}
const role =
invitation.requestedRole === 'admin'
? WorkspaceRole.Admin
: WorkspaceRole.Collaborator;
return await this.models.workspaceMember.setActive(
workspaceId,
userId,
role,
{ source: invitation.source }
);
}
return await this.updateInvitationStatus({
workspaceId,
userId,
status: invitationStatus,
inviterId: data.inviterId,View on GitHub (pinned to 26c515e050)
Solutions
- Re-issue the invitation and have the user accept the fresh link.
- If the user should be a member regardless of the missing invitation, call workspaceMember.setActive directly with an explicit role.
- Check findInvitation before transitioning to Accepted and branch accordingly.
Example fix
// before
await models.workspaceInvitation.setState(wsId, userId, WorkspaceMemberStatus.Accepted);
// after
const inv = await models.workspaceInvitation.findInvitation(wsId, userId);
if (!inv) {
await models.workspaceMember.setActive(wsId, userId, WorkspaceRole.Collaborator);
} else {
await models.workspaceInvitation.setState(wsId, userId, WorkspaceMemberStatus.Accepted);
} Defensive patterns
Strategy: validation
Validate before calling
async function acceptOrFail(models, wsId: string, userId: string) {
const inv = await models.workspaceInvitation.findInvitation(wsId, userId);
if (!inv) {
// no invitation to accept -> activate directly with an explicit role
await models.workspaceMember.setActive(wsId, userId, WorkspaceRole.Collaborator);
return;
}
await models.workspaceInvitation.setState(wsId, userId, WorkspaceMemberStatus.Accepted);
} Try / catch
try {
await models.workspaceInvitation.setState(wsId, userId, WorkspaceMemberStatus.Accepted);
} catch (e) {
if (e instanceof Error && e.message === 'Cannot activate a missing workspace invitation.') {
// re-issue invitation or activate the member directly with a known role
} else throw e;
} Prevention
- Check findInvitation before transitioning a user to Accepted.
- Revoke stale invitation links promptly so old tokens can't be replayed.
- For users added directly as active members, call setActive instead of setState(Accepted).
When it happens
Trigger: Calling setState(workspaceId, userId, Accepted) when findInvitation returns null — e.g. accepting an invitation that was already revoked/deleted, or accepting for a user who was added directly as an active member (so no invitation row exists).
Common situations: User clicks a stale/old invite link after it was revoked; a concurrent accept+cancel race deleted the invitation row; a migration left active members without corresponding invitation rows; replaying an already-consumed accept token.
Related errors
- invalid_invitation
- authentication_required
- not_found
- new_owner_is_not_active_member
- failed_to_save_updates
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/9fa73763b48b284a.
Report an issue: GitHub.