Skip to content

[PM-40744] Update Server Error Messages for VFO1 - #8087

Open
BTreston wants to merge 12 commits into
mainfrom
ac/PM-40744-User-facing-error-messages
Open

[PM-40744] Update Server Error Messages for VFO1#8087
BTreston wants to merge 12 commits into
mainfrom
ac/PM-40744-User-facing-error-messages

Conversation

@BTreston

@BTreston BTreston commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-40744

📔 Objective

Updates copy of user facing server error messages for VFO1. For reviewers, the copy changes are organized by commit, with the latter commits being pure refactors to consolidate inline error strings into typed errors.

📸 Screenshots

@BTreston BTreston added the t:feature Change Type - Feature Development label Jul 29, 2026
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.38403% with 70 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.72%. Comparing base (494c3e0) to head (7e0af69).
⚠️ Report is 10 commits behind head on main.

Files with missing lines Patch % Lines
...es/Organizations/CloudOrganizationSignUpCommand.cs 28.57% 15 Missing ⚠️
...nsole/OrganizationFeatures/Organizations/Errors.cs 72.22% 10 Missing ⚠️
.../AutomaticallyConfirmOrganizationUsersValidator.cs 0.00% 7 Missing ⚠️
...s/RestoreUser/v1/RestoreOrganizationUserCommand.cs 70.83% 6 Missing and 1 partial ⚠️
...uirements/Errors/SingleOrganizationPolicyErrors.cs 60.00% 6 Missing ⚠️
...InviteLinks/ConfirmOrganizationInviteLinkErrors.cs 50.00% 4 Missing ⚠️
...Console/OrganizationFeatures/InviteLinks/Errors.cs 63.63% 4 Missing ⚠️
...ers/RevokeUser/v1/RevokeOrganizationUserCommand.cs 20.00% 4 Missing ⚠️
...Features/OrganizationUsers/RevokeUser/v1/Errors.cs 40.00% 3 Missing ⚠️
...eatures/OrganizationUsers/RestoreUser/v1/Errors.cs 80.00% 2 Missing ⚠️
... and 7 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8087      +/-   ##
==========================================
+ Coverage   62.67%   62.72%   +0.05%     
==========================================
  Files        2296     2301       +5     
  Lines      100044   100217     +173     
  Branches     9003     9020      +17     
==========================================
+ Hits        62701    62863     +162     
- Misses      35161    35169       +8     
- Partials     2182     2185       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@BTreston BTreston added the ai-review Request a Claude code review label Jul 30, 2026
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Re-reviewed the VFO1 error-copy pass across AdminConsole, Billing, and Commercial provider code, focusing on the changes pushed since the last review. The organization.Nameorganization.DisplayName() switch requested in review is applied consistently across all four call sites (AcceptOrganizationInviteLinkCommand, ConfirmOrganizationInviteLinkValidator, GetOrganizationInviteCommand, AcceptOrgUserCommand), and organization is non-null on every non-optional path. I verified there are no dangling references to the renamed *Error records, the deleted DuplicateDomainException, or the removed OrganizationDomainAllowEmailChangeQuery constants; that MapEmailChangeError is the only message-based control flow and still matches the new strings; and that no remaining test asserts a replaced literal.

Code Review Details
  • ♻️ : Still open from the previous review — the remove flow imports the restore feature's namespace to reuse an identical "Users invalid." string, forcing conststatic readonly and leaving the constant consumed only by tests
    • src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/RemoveOrganizationUserCommand.cs:30

Also noted, not worth its own comment: AcceptOrgUserCommand now reaches into the InviteLinks namespace for OrganizationAccessRevoked — the same cross-feature error borrowing already described in the thread above, so no separate comment.

Comment thread src/Core/AdminConsole/OrganizationFeatures/Organizations/Errors.cs Outdated
@BTreston
BTreston marked this pull request as ready for review July 30, 2026 20:15
@BTreston
BTreston requested review from a team as code owners July 30, 2026 20:15
Comment thread src/Core/AdminConsole/OrganizationFeatures/Organizations/Errors.cs Outdated
sutProvider.GetDependency<IAutomaticUserConfirmationPolicyEnforcementHandler>()
.GetAutoConfirmPolicyViolation(Arg.Any<AutomaticUserConfirmationPolicyRequirement>(), Arg.Any<Guid>(), Arg.Any<bool>(), Arg.Any<int>())
.Returns(new UserCannotBelongToAnotherOrganization());
.Returns(new UserCannotBelongToAnotherOrganization(string.Empty));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ DEBT: The new bulk email lookup is never exercised — these tests pass identically with the fix reverted.

Details and fix

SetupPassingBulkDataStubs (line 45) does not stub IUserRepository.GetManyAsync, so NSubstitute returns an empty sequence, emailByUserId is empty, TryGetValue fails, and WithEmail falls through to _ => error — leaving the string.Empty the stub supplied. Combined with the type-only assertions (Assert.IsType<UserCannotBelongToAnotherOrganization>), nothing here would fail if IUserRepository, usersTask, and WithEmail were removed from the validator, which is the blank-email bug that was just fixed.

Stubbing the repository in the helper and asserting on the message closes the gap:

sutProvider.GetDependency<IUserRepository>()
    .GetManyAsync(Arg.Any<IEnumerable<Guid>>())
    .Returns(orgUsers.Select(ou => new User { Id = ou.UserId!.Value, Email = $"{ou.UserId}@example.com" }));

// then, in the violation tests:
Assert.Contains($"{userId}@example.com", results[0].AsError.Message);


public const string UserNotFoundErrorMessage = "User not found.";
public const string UsersInvalidErrorMessage = "Users invalid.";
public static readonly string UsersInvalidErrorMessage = new UsersInvalid().Message;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ DEBT: The remove flow now imports the restore feature's namespace to reuse an identical "Users invalid." string.

Details

UsersInvalid is declared in RestoreUser/v1/Errors.cs, so this file now carries using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.RestoreUser.v1; for an error that has nothing to do with restoring, and the rendered copy is unchanged either way. Two side effects:

  • UsersInvalidErrorMessage had to drop const for static readonly while its five neighbours stay const, so it can no longer be used in constant contexts (case labels, attributes).
  • Line 207 throws new UsersInvalid().Message instead of the constant declared right here, leaving the public field consumed only by RemoveOrganizationUserCommandTests.

Declaring UsersInvalid in the OrganizationUsers-level Errors.cs (or leaving this constant as-is, since the text did not change) keeps the feature folders independent. The same shape shows up at OrganizationUsersController.cs:784, where the bulk enable-secrets-manager endpoint reaches into UpdateUser.v2's errors.

@r-tome r-tome left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use organization.DisplayName() everywhere, otherwise looks good

if (!InviteLinkDomainValidator.IsEmailDomainAllowed(user.Email, link.GetAllowedDomains()))
{
return new EmailDomainNotAllowed();
return new EmailDomainNotAllowed(organization.Name);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should always use DisplayName()

Suggested change
return new EmailDomainNotAllowed(organization.Name);
return new EmailDomainNotAllowed(organization.DisplayName());

@BTreston
BTreston requested a review from r-tome July 31, 2026 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review t:feature Change Type - Feature Development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants