{"record":{"id":"1aa3f9f79379dc26","repo":"n8n-io/n8n","slug":"invalid-invite-url","errorCode":null,"errorMessage":"Invalid invite URL","messagePattern":"Invalid invite URL","errorType":"exception","errorClass":"BadRequestError","httpStatus":400,"severity":"error","filePath":"packages/cli/src/controllers/auth.controller.ts","lineNumber":247,"sourceCode":"\n\t\tif (!isWithinUsersLimit) {\n\t\t\tthis.logger.debug('Request to resolve signup token failed because of users quota reached', {\n\t\t\t\tinviterId,\n\t\t\t\tinviteeId,\n\t\t\t});\n\t\t\tthrow new ForbiddenError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);\n\t\t}\n\n\t\tconst users = await this.userRepository.findManyByIds([inviterId, inviteeId], {\n\t\t\tincludeRole: true,\n\t\t});\n\n\t\tif (users.length !== 2) {\n\t\t\tthis.logger.debug(\n\t\t\t\t'Request to resolve signup token failed because the ID of the inviter and/or the ID of the invitee were not found in database',\n\t\t\t\t{ inviterId, inviteeId },\n\t\t\t);\n\t\t\tthrow new BadRequestError('Invalid invite URL');\n\t\t}\n\n\t\tconst invitee = users.find((user) => user.id === inviteeId);\n\t\tif (!invitee || invitee.password) {\n\t\t\tthis.logger.error('Invalid invite URL - invitee already setup', {\n\t\t\t\tinviterId,\n\t\t\t\tinviteeId,\n\t\t\t});\n\t\t\tthrow new BadRequestError('The invitation was likely either deleted or already claimed');\n\t\t}\n\n\t\tconst inviter = users.find((user) => user.id === inviterId);\n\t\tif (!inviter?.email) {\n\t\t\tthis.logger.error(\n\t\t\t\t'Request to resolve signup token failed because inviter does not exist or is not set up',\n\t\t\t\t{\n\t\t\t\t\tinviterId: inviter?.id,\n\t\t\t\t},","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/cli/src/controllers/auth.controller.ts#L229-L265","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ask an admin to re-issue a fresh invitation — the referenced user IDs no longer exist and the link cannot be repaired.","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.","Audit deletion jobs that remove pending (passwordless) users to prevent recurrence.","Check the token is the original, unmodified URL from the invitation email — manual edits to query params produce this."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Before resolving, verify both user IDs exist\nimport type { User } from '@n8n/db';\n\nasync function idsExist(repo: { findManyByIds(ids: string[]): Promise<User[]> }, ids: string[]) {\n  const found = await repo.findManyByIds(ids);\n  return found.length === ids.length;\n}\n// only call resolve-signup-token when idsExist(repo, [inviterId, inviteeId])","typeGuard":null,"tryCatchPattern":"try {\n  await resolveSignupToken(token);\n} catch (e) {\n  if (e instanceof BadRequestError && e.message === 'Invalid invite URL') {\n    // re-issue the invitation; do not loop on the same token\n  }\n}","preventionTips":["Treat invite links as immutable; never hand-edit the token payload.","Cascade-delete invites when their inviter or invitee user is removed.","Audit pending invites after user-purge jobs."],"tags":["auth","invitation","bad-request","users"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}