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
- Shorten or hash the externalId value in the IdP attribute mapping to stay under 300 characters.
- Map externalId to a stable, short identifier such as the directory object GUID.
- 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
- Map externalId to a short stable GUID in the IdP, not a full DN.
- Enforce a 300-char client-side limit on all externalId values before sending.
- Document the 300-char limit in your SCIM integration guide.
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
- ExternalId cannot exceed 300 characters.
- ExternalId already exists for another group.
- Conflict.
- ExternalId already exists for another user.
- result.AsError.Message
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/9a304bdbbb9d7c53.
Report an issue: GitHub.