bitwarden/server · warning · BadRequestException

This user has already been invited.

Error message

This user has already been invited.

What it means

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.

Source

Thrown at src/Api/AdminConsole/Public/Controllers/MembersController.cs:169

    [HttpPost]
    [ProducesResponseType(typeof(MemberResponseModel), (int)HttpStatusCode.OK)]
    [ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)]
    public async Task<IActionResult> Post([FromBody] MemberCreateRequestModel model)
    {
        var hasStandaloneSecretsManager = false;

        var organization = await _organizationRepository.GetByIdAsync(_currentContext.OrganizationId!.Value);

        if (organization != null)
        {
            hasStandaloneSecretsManager = await _paymentService.HasSecretsManagerStandalone(organization);
        }

        var inviteRequest = model.ToInviteRequest(organization!, hasStandaloneSecretsManager, Guid.Empty, _timeProvider.GetUtcNow());
        var inviteResult = await _inviteOrganizationUsersCommand.InviteImportedOrganizationUsersAsync(inviteRequest) switch
        {
            Success<InviteOrganizationUsersResponse> success => success,
            Failure<InviteOrganizationUsersResponse> { Error.Message: NoUsersToInviteError.Code } => throw new BadRequestException("This user has already been invited."),
            Failure<InviteOrganizationUsersResponse> failure => throw MapToBitException(failure.Error),
            _ => throw new InvalidOperationException()
        };

        var user = inviteResult.Value.InvitedUsers.First();
        var collections = model.Collections?.Select(c => c.ToCollectionAccessSelection()).ToList();
        var response = new MemberResponseModel(user, collections);

        return new JsonResult(response);
    }

    /// <summary>
    /// Update a member.
    /// </summary>
    /// <remarks>
    /// Updates the specified member object. If a property is not provided,
    /// the value of the existing property will be reset.
    /// </remarks>

View on GitHub (pinned to e93b962371)

Solutions

  1. 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.
  2. If the member was deleted, restore or reactivate the existing record instead of re-inviting.
  3. De-duplicate the import batch so each email/externalId appears only once.
  4. Treat this 400 as informational in the SCIM client (the user is already provisioned) rather than a hard failure.

Example fix

// before
await scim.createUser({ emails: [{ value: email }] });
// after
const existing = await scim.listUsers(`userName eq "${email}"`);
if (existing.totalResults === 0) {
  await scim.createUser({ emails: [{ value: email }] });
}
Defensive patterns

Strategy: validation

Validate before calling

async function isAlreadyMember(scim, email) {
  const r = await scim.listUsers(`userName eq "${email}"`);
  return (r.totalResults ?? 0) > 0;
}

Type guard

function isInviteCandidate(v): v is { emails: { value: string }[] } {
  return !!v && Array.isArray(v.emails) && v.emails.some(e => !!e.value);
}

Try / catch

try { await scim.createUser(member); }
catch (e) {
  if (e?.response?.status === 400 && /already been invited/i.test(e.response.data?.Message ?? '')) return; // already provisioned
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13). Data as JSON: /api/errors/32c8ebcfb3b476b2. Report an issue: GitHub.