All in all there are some good points in your post, but i also have some points that are Not very good.
1. It's still possible to create an invalid UserId because of the constructor. I find it generally better to prevent any invalid instantiation by verifying in the init and throwing. This ensures all other places are 100÷ Safe when they receive a UserId.
2. Given a private constructor, the factory could either be an extension on String, because then you can follow the established convention String.toUserId which throws and String.toUserIdOrNull which catches and returns null. Or when you don't like std class Extensions, the factory could just live in the companion and return nullable or just Result of User or respectively Result of UserId.
3. For binary cases of success or failure, it's better to use nullability, sealed results are best for multiple options of outputs.
4. Dont name the method isUserValid when it's already on class User. isValid is better, called like user.isValid() or when the class only has vals, calculate it as a property in the class body, wo that it's called user.isValid. same for UserId.