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 PatchUserCommand.HandleExternalIdOperationAsync when the new externalId for a user exceeds 300 characters. This mirrors the same 300-character constraint enforced for groups. The OrganizationUser entity caps ExternalId at 300 characters.
Source
Thrown at bitwarden_license/src/Scim/Users/PatchUserCommand.cs:121
if (active && orgUser.Status == OrganizationUserStatusType.Revoked)
{
await _restoreOrganizationUserCommand.RestoreUserAsync(orgUser, EventSystemUser.SCIM);
return true;
}
else if (!active && orgUser.Status != OrganizationUserStatusType.Revoked)
{
await _revokeOrganizationUserCommand.RevokeUserAsync(orgUser, EventSystemUser.SCIM, RevocationReason.Manual);
return true;
}
return false;
}
private async Task HandleExternalIdOperationAsync(Core.Entities.OrganizationUser orgUser, string? newExternalId)
{
// Validate max length (300 chars per OrganizationUser.cs line 59)
if (!string.IsNullOrWhiteSpace(newExternalId) && newExternalId.Length > 300)
{
throw new BadRequestException("ExternalId cannot exceed 300 characters.");
}
// Check for duplicate externalId (same validation as PostUserCommand.cs)
if (!string.IsNullOrWhiteSpace(newExternalId))
{
var existingUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(orgUser.OrganizationId);
if (existingUsers.Any(u => u.Id != orgUser.Id &&
!string.IsNullOrWhiteSpace(u.ExternalId) &&
u.ExternalId.Equals(newExternalId, StringComparison.OrdinalIgnoreCase)))
{
throw new ConflictException("ExternalId already exists for another user.");
}
}
orgUser.ExternalId = newExternalId;
await _organizationUserRepository.ReplaceAsync(orgUser);
}
}View on GitHub (pinned to e93b962371)
Solutions
- Map externalId to a short, stable identifier such as the directory object GUID or SAM account name.
- Add a client-side length check before sending the PATCH.
- Trim or hash long identifiers in the IdP attribute transformation rules.
Example fix
// before: IdP maps full DN
// { "op":"replace", "path":"externalId", "value":"CN=...,OU=...,DC=...(300+)" }
// after: map to GUID
// { "op":"replace", "path":"externalId", "value":"a1b2c3d4-e5f6-7890-abcd-ef1234567890" } Defensive patterns
Strategy: validation
Validate before calling
const MAX_EXTERNAL_ID = 300;
if (!string.IsNullOrWhiteSpace(newExternalId) && newExternalId.Length > MAX_EXTERNAL_ID)
throw new InvalidOperationException($"externalId exceeds {MAX_EXTERNAL_ID} chars");
// only then PATCH Try / catch
try { await scimClient.PatchUserExternalIdAsync(orgId, userId, newExternalId); }
catch (ScimException ex) when (ex.StatusCode == 400 && ex.Message.Contains("ExternalId"))
{ /* shorten externalId and retry */ } Prevention
- Map externalId to a short stable identifier (object GUID) in the IdP.
- Enforce 300-char client-side limit.
- Document the constraint in your SCIM integration.
When it happens
Trigger: PATCH /v2/{organizationId}/Users/{id} with a 'replace' on 'externalId' whose value exceeds 300 characters. Caused by an IdP mapping a long attribute (e.g., full DN, concatenated GUIDs) to externalId.
Common situations: IdP maps a long directory identifier to externalId. Custom SCIM client sends a serialized object or URL as externalId. Deeply nested AD OUs produce long DNs.
Related errors
- ExternalId cannot exceed 300 characters.
- ExternalId already exists for another user.
- result.AsError.Message
- ExternalId already exists for another group.
- User not found.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/1e91e6cb38eb8918.
Report an issue: GitHub.