{"record":{"id":"668eb7d523040ed2","repo":"n8n-io/n8n","slug":"user-not-found","errorCode":null,"errorMessage":"User not found","messagePattern":"User not found","errorType":"exception","errorClass":"NotFoundError","httpStatus":404,"severity":"error","filePath":"packages/cli/src/controllers/users.controller.ts","lineNumber":159,"sourceCode":"\t\t\t\t};\n\t\t\t}),\n\t\t);\n\n\t\treturn usersListSchema.parse({\n\t\t\tcount,\n\t\t\titems: this.removeSupplementaryFields(publicUsers, listQueryOptions, req.user),\n\t\t});\n\t}\n\n\t@Get('/:id/password-reset-link')\n\t@GlobalScope('user:resetPassword')\n\tasync getUserPasswordResetLink(req: UserRequest.PasswordResetLink) {\n\t\tconst user = await this.userRepository.findOneOrFail({\n\t\t\twhere: { id: req.params.id },\n\t\t\trelations: ['role'],\n\t\t});\n\t\tif (!user) {\n\t\t\tthrow new NotFoundError('User not found');\n\t\t}\n\n\t\tif (\n\t\t\treq.user.role.slug === GLOBAL_ADMIN_ROLE.slug &&\n\t\t\tuser.role.slug === GLOBAL_OWNER_ROLE.slug\n\t\t) {\n\t\t\tthrow new ForbiddenError('Admin cannot reset password of global owner');\n\t\t}\n\n\t\tconst link = this.authService.generatePasswordResetUrl(user);\n\t\treturn { link };\n\t}\n\n\t@Post('/:id/invite-link')\n\t@GlobalScope('user:generateInviteLink')\n\tasync generateInviteLink(req: AuthenticatedRequest<{ id: string }, {}, {}, {}>, _res: Response) {\n\t\tconst inviterId = req.user.id;\n\t\tconst inviteeId = req.params.id;","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/cli/src/controllers/users.controller.ts#L141-L177","documentation":"Returned by GET /users/:id/password-reset-link (scope user:resetPassword) when the user lookup yields no row. Note the lookup uses TypeORM findOneOrFail (which already throws EntityNotFoundError on miss), so this explicit if(!user) branch is effectively unreachable defense-in-depth; in practice you will see it only if findOneOrFail semantics change or the call is refactored to findOne.","triggerScenarios":"Requesting a password reset link for an id that does not exist in the users table (deleted user, typo'd UUID, pending invite that has no user row yet), while authenticated as a user with the user:resetPassword global scope.","commonSituations":"Admin UI trying to reset a user that was hard-deleted between page load and click; copy-paste of an old user id; integration tests against a freshly migrated DB without seed users.","solutions":["Confirm the target user id exists (GET /users/:id) before requesting the reset link.","If the user was deleted, surface that to the admin rather than retrying the reset endpoint.","Treat a 404 here as terminal — do not retry without changing the id."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"async function ensureUserExists(id: string) {\n  const r = await fetch(`/rest/users/${id}`, { method: 'GET' });\n  if (r.status === 404) throw new Error(`User ${id} does not exist; cannot request reset link`);\n  if (!r.ok) throw new Error(`User lookup failed: ${r.status}`);\n}\n// await ensureUserExists(userId) before GET /users/:id/password-reset-link","typeGuard":null,"tryCatchPattern":"try {\n  await fetch(`/rest/users/${id}/password-reset-link`);\n} catch (e) {\n  if (e.statusCode === 404) { /* user is gone; do not retry */ }\n  else throw e;\n}","preventionTips":["Resolve users from a fresh GET /users list before offering the reset action.","Do not cache user existence across sessions.","Treat 404 from this endpoint as terminal."],"tags":["users","password-reset","rest-api","not-found","authorization"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}