bitwarden/server · error · BadRequestException

ExternalId cannot exceed 300 characters.

Error message

ExternalId cannot exceed 300 characters.

What it means

Thrown as a BadRequestException (HTTP 400) by PatchGroupCommand.EnsureExternalIdIsValidAsync when the new externalId string supplied in a SCIM PATCH operation exceeds 300 characters. ExternalId is a customer-managed identifier from the IdP and is constrained to 300 characters in the Group entity schema.

Source

Thrown at bitwarden_license/src/Scim/Groups/PatchGroupCommand.cs:197

    }

    private async Task<string> GetValidExternalIdAsync(Group group, string newExternalId)
    {
        if (string.IsNullOrWhiteSpace(newExternalId))
        {
            // Ensure we're not saving empty or just whitespace externalId.
            return null;
        }

        await EnsureExternalIdIsValidAsync(group, newExternalId);
        return newExternalId;
    }

    private async Task EnsureExternalIdIsValidAsync(Group group, string newExternalId)
    {
        if (newExternalId.Length > 300)
        {
            throw new BadRequestException("ExternalId cannot exceed 300 characters.");
        }

        var existingGroups = await _groupRepository.GetManyByOrganizationIdAsync(group.OrganizationId);
        if (existingGroups.Any(g => g.Id != group.Id &&
                                    !string.IsNullOrWhiteSpace(g.ExternalId) &&
                                    g.ExternalId.Equals(newExternalId, StringComparison.OrdinalIgnoreCase)))
        {
            throw new ConflictException("ExternalId already exists for another group.");
        }
    }

    private async Task AddMembersAsync(Group group, HashSet<Guid> usersToAdd)
    {
        // Azure Entra ID is known to send redundant "add" requests for each existing member every time any member
        // is removed. To avoid excessive load on the database, we check against the high availability replica and
        // return early if they already exist.
        var groupMembers = await _groupRepository.GetManyUserIdsByIdAsync(group.Id, useReadOnlyReplica: true);
        if (usersToAdd.IsSubsetOf(groupMembers))

View on GitHub (pinned to e93b962371)

Solutions

  1. Shorten or hash the externalId value in the IdP attribute mapping to stay under 300 characters.
  2. Map externalId to a stable, short identifier such as the directory object GUID.
  3. Add a pre-flight length check in your SCIM client before sending the PATCH.

Example fix

// before: IdP maps full DN to externalId
//   "externalId": "CN=Very Long Group Name,OU=Dept,OU=Groups,DC=corp,DC=example,DC=com,...(300+ chars)"
// after: map to object GUID
//   "externalId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
Defensive patterns

Strategy: validation

Validate before calling

const MAX_EXTERNAL_ID = 300;
if (externalId != null && externalId.Length > MAX_EXTERNAL_ID)
    throw new InvalidOperationException($"externalId exceeds {MAX_EXTERNAL_ID} chars");
// only then send PATCH

Try / catch

try { await scimClient.PatchGroupExternalIdAsync(orgId, groupId, externalId); }
catch (ScimException ex) when (ex.StatusCode == 400 && ex.Message.Contains("ExternalId"))
{ /* shorten the externalId and retry */ }

Prevention

When it happens

Trigger: SCIM PATCH /v2/{organizationId}/Groups/{id} with a 'replace' operation on 'externalId' whose value string is longer than 300 characters. Typically caused by an IdP generating excessively long opaque identifiers or a misconfigured attribute mapping.

Common situations: An IdP (e.g., Entra ID) maps a long composite attribute (e.g., a full DN or concatenated GUIDs) to externalId. A custom SCIM client serializes an object instead of a short ID. A directory with deep nested OUs produces very long DNs.

Related errors


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