n8n-io/n8n · error · BadRequestError

Invalid invite URL

Error message

Invalid invite URL

What it means

BadRequestError (HTTP 400) 'Invalid invite URL' thrown at auth.controller.ts:247 after `userRepository.findManyByIds([inviterId, inviteeId])` returns fewer than two rows. The token decoded to two IDs but at least one no longer exists in the `user` table — typical of an invite whose target/inviter row was hard-deleted between issuance and resolve.

Source

Thrown at packages/cli/src/controllers/auth.controller.ts:247

		if (!isWithinUsersLimit) {
			this.logger.debug('Request to resolve signup token failed because of users quota reached', {
				inviterId,
				inviteeId,
			});
			throw new ForbiddenError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);
		}

		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,
				},

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Ask an admin to re-issue a fresh invitation — the referenced user IDs no longer exist and the link cannot be repaired.
  2. Verify in the DB that both `inviterId` and `inviteeId` still exist in the `user` table; if the invitee was deleted, recreate the invite from Settings > Users.
  3. Audit deletion jobs that remove pending (passwordless) users to prevent recurrence.
  4. Check the token is the original, unmodified URL from the invitation email — manual edits to query params produce this.
Defensive patterns

Strategy: try-catch

Validate before calling

// Before resolving, verify both user IDs exist
import type { User } from '@n8n/db';

async function idsExist(repo: { findManyByIds(ids: string[]): Promise<User[]> }, ids: string[]) {
  const found = await repo.findManyByIds(ids);
  return found.length === ids.length;
}
// only call resolve-signup-token when idsExist(repo, [inviterId, inviteeId])

Try / catch

try {
  await resolveSignupToken(token);
} catch (e) {
  if (e instanceof BadRequestError && e.message === 'Invalid invite URL') {
    // re-issue the invitation; do not loop on the same token
  }
}

Prevention

When it happens

Trigger: Resolving a signup token where the invitee or inviter was deleted from the DB (e.g. admin purged the pending user, or the inviter left and was removed). Also reproducible by manually mutating the token payload to reference non-existent user IDs.

Common situations: Old invite links lingering after a tenant cleanup; GDPR/data-deletion jobs that prune pending invitees; stale email after the inviter's account was offboarded; forged or truncated tokens.

Related errors


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