n8n-io/n8n · error · BadRequestError
The invitation was likely either deleted or already claimed
Error message
The invitation was likely either deleted or already claimed
What it means
BadRequestError (HTTP 400) 'The invitation was likely either deleted or already claimed' thrown at auth.controller.ts:256 when the invitee row exists but either is falsy or already has a `password` set. Once an invitee completes signup and stores a password, the invite is considered claimed and the resolve endpoint refuses to re-enter the setup flow.
Source
Thrown at packages/cli/src/controllers/auth.controller.ts:256
const users = await this.userRepository.findManyByIds([inviterId, inviteeId], {
includeRole: true,
});
if (users.length !== 2) {
this.logger.debug(
'Request to resolve signup token failed because the ID of the inviter and/or the ID of the invitee were not found in database',
{ inviterId, inviteeId },
);
throw new BadRequestError('Invalid invite URL');
}
const invitee = users.find((user) => user.id === inviteeId);
if (!invitee || invitee.password) {
this.logger.error('Invalid invite URL - invitee already setup', {
inviterId,
inviteeId,
});
throw new BadRequestError('The invitation was likely either deleted or already claimed');
}
const inviter = users.find((user) => user.id === inviterId);
if (!inviter?.email) {
this.logger.error(
'Request to resolve signup token failed because inviter does not exist or is not set up',
{
inviterId: inviter?.id,
},
);
throw new BadRequestError('Invalid request');
}
this.eventService.emit('user-invite-email-click', { inviter, invitee });
const { firstName, lastName } = inviter;
return { inviter: { firstName, lastName } };
}View on GitHub (pinned to 5ac6606e81)
Solutions
- If not yet registered: ask an admin to delete the partially-set-up invitee row and re-invite, producing a fresh token.
- If already registered: use the normal 'Forgot password' flow instead of the invite link.
- Admin: in Settings > Users confirm the invitee's status — a password column set to non-null means claimed.
- Communicate to invitees that the link is single-use and to complete signup in one sitting.
Defensive patterns
Strategy: validation
Validate before calling
// Before opening invite link, check the invitee row
async function isUnclaimed(user: { password: string | null } | null) {
return !!user && !user.password;
}
// gate the resolve call on isUnclaimed(await userRepo.findOneBy({ id: inviteeId })) Type guard
const isUnclaimedInvitee = (u: unknown): u is { id: string; password: null } =>
typeof u === 'object' && u !== null && 'id' in u && 'password' in u && (u as any).password === null; Try / catch
try {
await resolveSignupToken(token);
} catch (e) {
if (e instanceof BadRequestError && /deleted or already claimed/.test(e.message)) {
// route the user to 'Forgot password' if they already have an account
}
} Prevention
- Tell invitees the link is single-use and to finish signup in one sitting.
- Auto-invalidate the invite token once a password is set.
- Surface 'already claimed' to the user with a login link instead of an error page.
When it happens
Trigger: User clicks an old invite link after already completing signup once (e.g. re-clicking after password set, or a duplicate link in mailbox). Also when the invitee row was soft-deleted/marked pending but a password already exists.
Common situations: Invitee reopens the welcome email days later; forwarded invites where two people clicked; password reset flows that mistakenly route back through the invite resolve URL; bookmarked invite links.
Related errors
- Invalid invite URL
- Invalid request
- Maximum number of users reached
- Models couldn't be loaded. Check that the selected credentia
- OAuth access token expired and no refresh token is available
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/744feb4efde45539.
Report an issue: GitHub.