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

  1. If not yet registered: ask an admin to delete the partially-set-up invitee row and re-invite, producing a fresh token.
  2. If already registered: use the normal 'Forgot password' flow instead of the invite link.
  3. Admin: in Settings > Users confirm the invitee's status — a password column set to non-null means claimed.
  4. 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

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


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/744feb4efde45539. Report an issue: GitHub.