{"record":{"id":"1e91e6cb38eb8918","repo":"bitwarden/server","slug":"externalid-cannot-exceed-300-characters-1e91e6","errorCode":null,"errorMessage":"ExternalId cannot exceed 300 characters.","messagePattern":"ExternalId cannot exceed 300 characters\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"bitwarden_license/src/Scim/Users/PatchUserCommand.cs","lineNumber":121,"sourceCode":"        if (active && orgUser.Status == OrganizationUserStatusType.Revoked)\n        {\n            await _restoreOrganizationUserCommand.RestoreUserAsync(orgUser, EventSystemUser.SCIM);\n            return true;\n        }\n        else if (!active && orgUser.Status != OrganizationUserStatusType.Revoked)\n        {\n            await _revokeOrganizationUserCommand.RevokeUserAsync(orgUser, EventSystemUser.SCIM, RevocationReason.Manual);\n            return true;\n        }\n        return false;\n    }\n\n    private async Task HandleExternalIdOperationAsync(Core.Entities.OrganizationUser orgUser, string? newExternalId)\n    {\n        // Validate max length (300 chars per OrganizationUser.cs line 59)\n        if (!string.IsNullOrWhiteSpace(newExternalId) && newExternalId.Length > 300)\n        {\n            throw new BadRequestException(\"ExternalId cannot exceed 300 characters.\");\n        }\n\n        // Check for duplicate externalId (same validation as PostUserCommand.cs)\n        if (!string.IsNullOrWhiteSpace(newExternalId))\n        {\n            var existingUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(orgUser.OrganizationId);\n            if (existingUsers.Any(u => u.Id != orgUser.Id &&\n                !string.IsNullOrWhiteSpace(u.ExternalId) &&\n                u.ExternalId.Equals(newExternalId, StringComparison.OrdinalIgnoreCase)))\n            {\n                throw new ConflictException(\"ExternalId already exists for another user.\");\n            }\n        }\n\n        orgUser.ExternalId = newExternalId;\n        await _organizationUserRepository.ReplaceAsync(orgUser);\n    }\n}","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/bitwarden_license/src/Scim/Users/PatchUserCommand.cs#L103-L139","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: IdP maps full DN\n//   { \"op\":\"replace\", \"path\":\"externalId\", \"value\":\"CN=...,OU=...,DC=...(300+)\" }\n// after: map to GUID\n//   { \"op\":\"replace\", \"path\":\"externalId\", \"value\":\"a1b2c3d4-e5f6-7890-abcd-ef1234567890\" }","handlingStrategy":"validation","validationCode":"const MAX_EXTERNAL_ID = 300;\nif (!string.IsNullOrWhiteSpace(newExternalId) && newExternalId.Length > MAX_EXTERNAL_ID)\n    throw new InvalidOperationException($\"externalId exceeds {MAX_EXTERNAL_ID} chars\");\n// only then PATCH","typeGuard":null,"tryCatchPattern":"try { await scimClient.PatchUserExternalIdAsync(orgId, userId, newExternalId); }\ncatch (ScimException ex) when (ex.StatusCode == 400 && ex.Message.Contains(\"ExternalId\"))\n{ /* shorten externalId and retry */ }","preventionTips":["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."],"tags":["scim","validation","users","http-400","externalid","patch"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}