{"record":{"id":"a72912a5f888c9fd","repo":"strapi/strapi","slug":"email-already-taken-a72912","errorCode":null,"errorMessage":"Email already taken","messagePattern":"Email already taken","errorType":"exception","errorClass":"ApplicationError","httpStatus":400,"severity":"error","filePath":"packages/core/admin/server/src/controllers/user.ts","lineNumber":44,"sourceCode":"    const { body } = ctx.request as Create.Request;\n    const cleanData = { ...body, email: _.get(body, `email`, ``).toLowerCase() };\n\n    await validateUserCreationInput(cleanData);\n\n    const attributes = _.pick(cleanData, [\n      'firstname',\n      'lastname',\n      'email',\n      'roles',\n      'preferedLanguage',\n    ]);\n\n    const userAlreadyExists = await getService('user').exists({\n      email: attributes.email,\n    });\n\n    if (userAlreadyExists) {\n      throw new ApplicationError('Email already taken');\n    }\n\n    const createdUser = await getService('user').create(attributes);\n\n    const userInfo = getService('user').sanitizeUser(createdUser);\n\n    // Note: We need to assign manually the registrationToken to the\n    // final user payload so that it's not removed in the sanitation process.\n    Object.assign(userInfo, { registrationToken: createdUser.registrationToken });\n\n    // Send 201 created\n    ctx.created({ data: userInfo } satisfies Create.Response);\n  },\n\n  async find(ctx: Context) {\n    const userService = getService('user');\n\n    const permissionsManager = strapi.service('admin::permission').createPermissionsManager({","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/strapi/strapi/blob/4a4101264d7098754df36e85fa629fd2f2349d8c/packages/core/admin/server/src/controllers/user.ts#L26-L62","documentation":"Thrown by the user create controller when a user with the supplied email already exists. The controller picks the email from the parsed body, calls getService('user').exists({ email }), and on a truthy result throws ApplicationError('Email already taken') at user.ts:50. Admin users are uniquely keyed by email because it is the login identifier.","triggerScenarios":"POST /admin/users with body.email matching an existing admin::user.email. Fires after input parse/normalize and before getService('user').create.","commonSituations":"Re-running a user-provisioning script that creates 'admin@ex.com' on every boot; inviting the same person twice; seeding a default admin in bootstrap after one already exists; case variants that normalize to the same address.","solutions":["Use a distinct email address for the new user.","Look up the existing user by email first; if present, update or re-send their invitation instead of creating.","Ensure email normalization (lowercasing) is applied before the existence check so case-only differences are caught client-side."],"exampleFix":"// before\nawait fetch('/admin/users', { method: 'POST', body: JSON.stringify({ email: 'admin@ex.com', ... }) });\n\n// after\nconst [existing] = await strapi.query('admin::user').findMany({ where: { email: 'admin@ex.com'.toLowerCase() } });\nif (existing) {\n  await strapi.service('admin::user').sendRegistration(existing);\n} else {\n  await fetch('/admin/users', { method: 'POST', body: JSON.stringify({ email: 'admin@ex.com', ... }) });\n}","handlingStrategy":"validation","validationCode":"const email = attributes.email.toLowerCase().trim();\nconst taken = await strapi.service('admin::user').exists({ email });\nif (taken) throw new Error(`User email \"${email}\" already taken`);","typeGuard":"const isUniqueUserEmail = async (email) => !(await strapi.service('admin::user').exists({ email: email.toLowerCase().trim() }));","tryCatchPattern":"try {\n  await getService('user').create(attributes);\n} catch (e) {\n  if (e instanceof ApplicationError && /Email already taken/.test(e.message)) {\n    // update existing user or re-send invitation\n  } else throw e;\n}","preventionTips":["Make user-provisioning scripts idempotent by email.","Normalize (lowercase, trim) emails before the existence check.","Re-send invitations for existing users instead of recreating."],"tags":["admin","user","email","uniqueness","validation"],"backgroundTag":null,"analyzedSha":"4a4101264d7098754df36e85fa629fd2f2349d8c","analyzedAt":"2026-08-12T12:47:50.760Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}