{"record":{"id":"32c8ebcfb3b476b2","repo":"bitwarden/server","slug":"this-user-has-already-been-invited","errorCode":null,"errorMessage":"This user has already been invited.","messagePattern":"This user has already been invited\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"src/Api/AdminConsole/Public/Controllers/MembersController.cs","lineNumber":169,"sourceCode":"    [HttpPost]\n    [ProducesResponseType(typeof(MemberResponseModel), (int)HttpStatusCode.OK)]\n    [ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)]\n    public async Task<IActionResult> Post([FromBody] MemberCreateRequestModel model)\n    {\n        var hasStandaloneSecretsManager = false;\n\n        var organization = await _organizationRepository.GetByIdAsync(_currentContext.OrganizationId!.Value);\n\n        if (organization != null)\n        {\n            hasStandaloneSecretsManager = await _paymentService.HasSecretsManagerStandalone(organization);\n        }\n\n        var inviteRequest = model.ToInviteRequest(organization!, hasStandaloneSecretsManager, Guid.Empty, _timeProvider.GetUtcNow());\n        var inviteResult = await _inviteOrganizationUsersCommand.InviteImportedOrganizationUsersAsync(inviteRequest) switch\n        {\n            Success<InviteOrganizationUsersResponse> success => success,\n            Failure<InviteOrganizationUsersResponse> { Error.Message: NoUsersToInviteError.Code } => throw new BadRequestException(\"This user has already been invited.\"),\n            Failure<InviteOrganizationUsersResponse> failure => throw MapToBitException(failure.Error),\n            _ => throw new InvalidOperationException()\n        };\n\n        var user = inviteResult.Value.InvitedUsers.First();\n        var collections = model.Collections?.Select(c => c.ToCollectionAccessSelection()).ToList();\n        var response = new MemberResponseModel(user, collections);\n\n        return new JsonResult(response);\n    }\n\n    /// <summary>\n    /// Update a member.\n    /// </summary>\n    /// <remarks>\n    /// Updates the specified member object. If a property is not provided,\n    /// the value of the existing property will be reset.\n    /// </remarks>","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/AdminConsole/Public/Controllers/MembersController.cs#L151-L187","documentation":"Thrown as a 400 BadRequestException(\"This user has already been invited.\") from the public/SCIM MembersController invite-import path when _inviteOrganizationUsersCommand.InviteImportedOrganizationUsersAsync returns a Failure whose Error.Message equals NoUsersToInviteError.Code (\"No users to invite\"). That failure means every imported member already has an existing or pending organization-user record, so there was nothing new to invite.","triggerScenarios":"POST /scim/v2/{orgId}/users (or public members invite-import) for a single member whose email/externalId already maps to an invited or confirmed organization user. The command short-circuits with NoUsersToInviteError and the controller maps it to this 400.","commonSituations":"IdP/SCIM provisioning re-sends a user that is already invited or active; the member was previously deleted but not purged so the record still resolves; duplicate email in the import batch where the first occurrence consumed the invite.","solutions":["Before inviting, query existing organization users (GET the member list / SCIM Users with a filter on the email or externalId) and skip members already present.","If the member was deleted, restore or reactivate the existing record instead of re-inviting.","De-duplicate the import batch so each email/externalId appears only once.","Treat this 400 as informational in the SCIM client (the user is already provisioned) rather than a hard failure."],"exampleFix":"// before\nawait scim.createUser({ emails: [{ value: email }] });\n// after\nconst existing = await scim.listUsers(`userName eq \"${email}\"`);\nif (existing.totalResults === 0) {\n  await scim.createUser({ emails: [{ value: email }] });\n}","handlingStrategy":"validation","validationCode":"async function isAlreadyMember(scim, email) {\n  const r = await scim.listUsers(`userName eq \"${email}\"`);\n  return (r.totalResults ?? 0) > 0;\n}","typeGuard":"function isInviteCandidate(v): v is { emails: { value: string }[] } {\n  return !!v && Array.isArray(v.emails) && v.emails.some(e => !!e.value);\n}","tryCatchPattern":"try { await scim.createUser(member); }\ncatch (e) {\n  if (e?.response?.status === 400 && /already been invited/i.test(e.response.data?.Message ?? '')) return; // already provisioned\n  throw e;\n}","preventionTips":["Check existing members by email/externalId before inviting.","De-duplicate import batches.","Restore deleted-but-not-purged members instead of re-inviting.","Treat 'already invited' as success in SCIM sync loops."],"tags":["bitwarden","scim","invite","organization-users","idempotency","aspnet"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}