You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[$175] WS settings - Error appear when changing role just after adding User #103058
The Role page finds the member using the accountID in its URL. For a brand-new invitee, that ID is a temporary (optimistic) ID. When the AddMembersToWorkspace response arrives, the app deletes the temporary personal-details entry. The Role page then can't find the member's login or role. It blocks itself and shows the "Hmm... it's not here" page.
WorkspaceMemberDetailsRolePage.tsx:40 resolves the login with personalDetails?.[accountID]?.login ?? ''. After the swap, the login is '' and the member is undefined.
Add a Role page UI test modeled on the stale optimistic accountID test in tests/ui/WorkspaceMemberDetailsPageTest.tsx.
What alternative solutions did you explore? (Optional)
Make pending-invite rows non-clickable, or disable the Role row while pendingAction === ADD. The members table already sets isInteractive: !details.isOptimisticPersonalDetail (WorkspaceMembersPage.tsx:447). The new table row from Update the workspace members table to the new style ignores that flag. This alone doesn't help with deep links, refreshes, or slow-loading real personal details, so it should only supplement the fix above.
Reproduction attempt and confidence
Confidence: medium. The code path is confirmed. The error was not reproduced on a local web dev build.
Fast path: On the local dev server, the invite request finished before the Role page opened, so the race never happened.
Offline path: We invited a member while offline and opened the Role page while the invite was pending. The page rendered fine because the temporary entry still existed. We then left the Role page before going back online. The failing case needs the Role page to stay open while the response arrives, and that wasn't tested.
Not a backend rejection: The report only needs a click on Role, with no role selected. A failed UpdateWorkspaceMembersRole would show workspace.editor.genericFailureMessage instead.
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/103058) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
changed the title [-]WS settings - Error appear when changing role just after adding User[/-][+][$175] WS settings - Error appear when changing role just after adding User[/+]on Oct 5, 2026
Please re-state the problem that we are trying to solve in this issue.
In Workspace Settings > Members, immediately after inviting a new member, if an admin opens that member's details and taps "Role" to change their role while the invite response settles, the page displays the "Hmm... it's not here" (not found / blocked) screen instead of the role selection list.
What is the root cause of that problem?
When inviting a new user to a workspace, the app optimistically generates an account ID (generateAccountID(login)). The navigation route to member details and subsequent subpages (including Role) receives this optimistic accountID in route.params.
When the backend AddMembersToWorkspace API response resolves, Onyx updates the member's data with their permanent account ID, and optimistic personal details are removed from personalDetails.
In src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx:139, PR #96449 introduced getMemberLoginByOptimisticAccountID(policy, routeAccountID) and usePersonalDetailByLogin(memberLogin) so the parent details page can resolve the member login and permanent account ID across this optimistic-to-permanent ID swap.
The Role page only attempts a direct lookup in personalDetails?.[accountID]?.login. Once the optimistic personal details entry is cleared, memberLogin falls back to '', member becomes undefined, and canMemberAssignRole returns false.
Because canManageSelectedMemberRole is false, WorkspaceMemberDetailsRolePage.tsx:76 activates shouldBeBlocked={!canManageSelectedMemberRole} inside AccessOrNotFoundWrapper, triggering the "Hmm... it's not here" blocked screen.
What changes do you think we should make in order to solve the problem?
Promote getMemberLoginByOptimisticAccountID to a shared helper:
Extract getMemberLoginByOptimisticAccountID(policy, accountID) from WorkspaceMemberDetailsPage.tsx into src/libs/PolicyUtils.ts so all workspace member subpages can consume it consistently.
Use resolvedAccountID in updateWorkspaceMembersRole(policy, [memberLogin], [resolvedAccountID], value), in isRuleBotEnforcingRules(resolvedAccountID, policy), and in the Navigation.goBack return route.
Protect sibling member subpages:
Apply the same getMemberLoginByOptimisticAccountID fallback in WorkArrangementPage.tsx:59 and WorkspaceMemberCustomFieldPage.tsx:46 to eliminate the same race condition when navigating directly to work arrangement or custom fields immediately after an invite.
Automated UI Unit Testing:
In tests/ui/WorkspaceMemberDetailsPageTest.tsx (or an equivalent WorkspaceMemberDetailsRolePageTest.tsx), add a test verifying that when WorkspaceMemberDetailsRolePage is rendered with a route holding a stale optimistic accountID (where personalDetails no longer contains that optimistic ID), the page resolves the employee role correctly and renders the role selection options without triggering the blocked screen.
What alternative solutions did you explore? (Optional)
Disabling interaction on the member row while pendingAction === ADD:
While isInteractive: !details.isOptimisticPersonalDetail exists in parts of the members table, restricting user navigation is a workaround that hurts responsiveness and does not protect against deep links, back navigation, or tab restoration while an invite request is settling over slow networks. Recovering the login identity via getMemberLoginByOptimisticAccountID addresses the root cause directly across the entire lifecycle.
The Role page keeps the optimistic accountID in the route, but after AddMembersToWorkspace resolves that temporary personal-details entry is replaced. Its direct lookup then returns no login/member, so AccessOrNotFoundWrapper blocks the page. The parent Member Details page already handles this race via the #96449 optimistic-ID fallback.
Fix
Reuse that fallback in WorkspaceMemberDetailsRolePage, resolve the permanent accountID with usePersonalDetailByLogin, and use the resolved identity for permission checks, isRuleBotEnforcingRules, updateWorkspaceMembersRole, and back navigation.
Test
Keep the Role page mounted while Onyx swaps optimistic -> real accountID; assert it never shows "Hmm... it's not here" and the role update uses the permanent accountID.
I would keep the production patch scoped to the Role flow unless a regression test proves another subpage needs the same change.
Please re-state the problem that we are trying to solve in this issue.
When inviting a new (non-existing Expensify) member to a workspace, navigating into the member row and clicking on "Role" quickly triggers a "Not Found" error / blocking screen instead of displaying the role options.
What is the root cause of that problem?
When an admin invites a new user, NewDot assigns an optimistic accountID (generated via generateAccountID(login)).
When the backend AddMembersToWorkspace call finishes:
The backend provides the permanent accountID, removing the temporary personal details entry.
In src/pages/workspace/members/WorkspaceMemberDetailsRolePage.tsx (line 40):
WorkspaceMemberDetailsRolePage looks up personalDetails?.[accountID]. Since accountID in route params is the optimistic ID, personalDetails?.[accountID] is undefined.
memberLogin falls back to '', so member is undefined, and canMemberAssignRole evaluates to false.
AccessOrNotFoundWrapper has shouldBeBlocked={!canManageSelectedMemberRole}, immediately blocking the screen with an error.
While WorkspaceMemberDetailsPage.tsx already addressed this in PR #96449 using getMemberLoginByOptimisticAccountID, this helper was not exported nor used in WorkspaceMemberDetailsRolePage.tsx.
What changes do you think we should make in order to solve the problem?
Export getMemberLoginByOptimisticAccountID in src/libs/PolicyUtils/index.ts (or src/libs/PolicyMemberRoleUtils.ts):
In src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx:
Import getMemberLoginByOptimisticAccountID from @libs/PolicyUtils instead of inlining it.
What alternative solutions did you explore? (Optional)
Forcing a blocking modal until AddMembersToWorkspace finishes: Poor UX; optimism is a core principle in New Expensify. Recovering the member login preserves instant responsiveness.
What specific scenarios should we cover in automated tests?
In tests/ui/WorkspaceMemberDetailsRolePageTest.tsx:
Navigating to WorkspaceMemberDetailsRolePage with an optimistic accountID successfully resolves the member from policy.employeeList and renders the role list.
Changing role with an optimistic ID dispatches updateWorkspaceMembersRole with the resolved ID and navigates back cleanly without errors.
The PR implements the proposal. The Role, Work arrangement, and custom field pages now find the member even when the route still holds the optimistic accountID. A new Role page UI test covers this case.
The automated web smoke test couldn't run because the test browser showed a blank page. Please test it manually before review.
Activity
applause-bot commentedon Oct 5, 2026
While Applause doesn't embed attachments for this bug, here are the links for quick view:
https://platform.applause.com/services/links/v1/external/31c2ac10b6fadfcbe2cb7d516a568ab917ee4aff045b4a40f971cfd5be9384bf
MelvinBot commentedon Oct 5, 2026
Proposal
What is the root cause of that problem?
The Role page finds the member using the accountID in its URL. For a brand-new invitee, that ID is a temporary (optimistic) ID. When the
AddMembersToWorkspaceresponse arrives, the app deletes the temporary personal-details entry. The Role page then can't find the member's login or role. It blocks itself and shows the "Hmm... it's not here" page.WorkspaceMemberDetailsRolePage.tsx:40resolves the login withpersonalDetails?.[accountID]?.login ?? ''. After the swap, the login is''and the member isundefined.PolicyUtils.ts:299-302:canMemberAssignRolereturnsfalsewhen there is no role. SoshouldBeBlockedatWorkspaceMemberDetailsRolePage.tsx:76istrue.The member details page already handles this case. Keep workspace member details resolvable after an invite completes added
getMemberLoginByOptimisticAccountID(WorkspaceMemberDetailsPage.tsx:95, used at:131). That fix never reached the Role page.What changes do you think we should make in order to solve the problem?
getMemberLoginByOptimisticAccountIDfromWorkspaceMemberDetailsPage.tsxinto a shared util, for examplePolicyUtils.ts.WorkspaceMemberDetailsRolePage, resolve the member the same way the details page does:usePersonalDetailByLogin(memberLogin).updateWorkspaceMembersRole,isRuleBotEnforcingRules, and the back routes.WorkArrangementPage.tsx:59shows the same not-found page.WorkspaceMemberCustomFieldPage.tsx:46isn't blocked, so saving would send an empty login.tests/ui/WorkspaceMemberDetailsPageTest.tsx.What alternative solutions did you explore? (Optional)
Make pending-invite rows non-clickable, or disable the Role row while
pendingAction === ADD. The members table already setsisInteractive: !details.isOptimisticPersonalDetail(WorkspaceMembersPage.tsx:447). The new table row from Update the workspace members table to the new style ignores that flag. This alone doesn't help with deep links, refreshes, or slow-loading real personal details, so it should only supplement the fix above.Reproduction attempt and confidence
UpdateWorkspaceMembersRolewould showworkspace.editor.genericFailureMessageinstead.Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/103058)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
view run · view recording
melvin-bot commentedon Oct 5, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @marufsharifi (
External)[-]WS settings - Error appear when changing role just after adding User[/-][+][$175] WS settings - Error appear when changing role just after adding User[/+]melvin-bot commentedon Oct 5, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022107143301856965003
camesenin commentedon Oct 5, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
In Workspace Settings > Members, immediately after inviting a new member, if an admin opens that member's details and taps "Role" to change their role while the invite response settles, the page displays the "Hmm... it's not here" (not found / blocked) screen instead of the role selection list.
What is the root cause of that problem?
generateAccountID(login)). The navigation route to member details and subsequent subpages (including Role) receives this optimisticaccountIDinroute.params.AddMembersToWorkspaceAPI response resolves, Onyx updates the member's data with their permanent account ID, and optimistic personal details are removed frompersonalDetails.src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx:139, PR #96449 introducedgetMemberLoginByOptimisticAccountID(policy, routeAccountID)andusePersonalDetailByLogin(memberLogin)so the parent details page can resolve the member login and permanent account ID across this optimistic-to-permanent ID swap.src/pages/workspace/members/WorkspaceMemberDetailsRolePage.tsx:39-41:personalDetails?.[accountID]?.login. Once the optimistic personal details entry is cleared,memberLoginfalls back to'',memberbecomesundefined, andcanMemberAssignRolereturnsfalse.canManageSelectedMemberRoleisfalse,WorkspaceMemberDetailsRolePage.tsx:76activatesshouldBeBlocked={!canManageSelectedMemberRole}insideAccessOrNotFoundWrapper, triggering the "Hmm... it's not here" blocked screen.WorkArrangementPage.tsx:59andWorkspaceMemberCustomFieldPage.tsx:46.What changes do you think we should make in order to solve the problem?
Promote
getMemberLoginByOptimisticAccountIDto a shared helper:Extract
getMemberLoginByOptimisticAccountID(policy, accountID)fromWorkspaceMemberDetailsPage.tsxintosrc/libs/PolicyUtils.tsso all workspace member subpages can consume it consistently.Update
src/pages/workspace/members/WorkspaceMemberDetailsRolePage.tsx:memberLoginwith fallback togetMemberLoginByOptimisticAccountID:resolvedAccountIDinupdateWorkspaceMembersRole(policy, [memberLogin], [resolvedAccountID], value), inisRuleBotEnforcingRules(resolvedAccountID, policy), and in theNavigation.goBackreturn route.Protect sibling member subpages:
Apply the same
getMemberLoginByOptimisticAccountIDfallback inWorkArrangementPage.tsx:59andWorkspaceMemberCustomFieldPage.tsx:46to eliminate the same race condition when navigating directly to work arrangement or custom fields immediately after an invite.Automated UI Unit Testing:
In
tests/ui/WorkspaceMemberDetailsPageTest.tsx(or an equivalentWorkspaceMemberDetailsRolePageTest.tsx), add a test verifying that whenWorkspaceMemberDetailsRolePageis rendered with a route holding a stale optimisticaccountID(wherepersonalDetailsno longer contains that optimistic ID), the page resolves the employee role correctly and renders the role selection options without triggering the blocked screen.What alternative solutions did you explore? (Optional)
pendingAction === ADD:While
isInteractive: !details.isOptimisticPersonalDetailexists in parts of the members table, restricting user navigation is a workaround that hurts responsiveness and does not protect against deep links, back navigation, or tab restoration while an invite request is settling over slow networks. Recovering the login identity viagetMemberLoginByOptimisticAccountIDaddresses the root cause directly across the entire lifecycle.Contributor details
Your Expensify account email: [email protected]
Upwork Profile Link: https://www.upwork.com/freelancers/~0170d6b02e9440e604
melvin-bot commentedon Oct 5, 2026
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
franklincg commentedon Oct 6, 2026
Proposal
Root cause
The Role page keeps the optimistic accountID in the route, but after AddMembersToWorkspace resolves that temporary personal-details entry is replaced. Its direct lookup then returns no login/member, so AccessOrNotFoundWrapper blocks the page. The parent Member Details page already handles this race via the #96449 optimistic-ID fallback.
Fix
Reuse that fallback in WorkspaceMemberDetailsRolePage, resolve the permanent accountID with usePersonalDetailByLogin, and use the resolved identity for permission checks, isRuleBotEnforcingRules, updateWorkspaceMembersRole, and back navigation.
Test
Keep the Role page mounted while Onyx swaps optimistic -> real accountID; assert it never shows "Hmm... it's not here" and the role update uses the permanent accountID.
I would keep the production patch scoped to the Role flow unless a regression test proves another subpage needs the same change.
melvin-bot commentedon Oct 8, 2026
@marufsharifi Whoops! This issue is 2 days overdue. Let's get this updated quick!
marufsharifi commentedon Oct 9, 2026
checking this today.
jloa-dev commentedon Oct 11, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
When inviting a new (non-existing Expensify) member to a workspace, navigating into the member row and clicking on "Role" quickly triggers a "Not Found" error / blocking screen instead of displaying the role options.
What is the root cause of that problem?
When an admin invites a new user, NewDot assigns an optimistic
accountID(generated viagenerateAccountID(login)).When the backend
AddMembersToWorkspacecall finishes:accountID, removing the temporary personal details entry.src/pages/workspace/members/WorkspaceMemberDetailsRolePage.tsx(line 40):WorkspaceMemberDetailsRolePagelooks uppersonalDetails?.[accountID]. SinceaccountIDin route params is the optimistic ID,personalDetails?.[accountID]isundefined.memberLoginfalls back to'', somemberisundefined, andcanMemberAssignRoleevaluates tofalse.AccessOrNotFoundWrapperhasshouldBeBlocked={!canManageSelectedMemberRole}, immediately blocking the screen with an error.While
WorkspaceMemberDetailsPage.tsxalready addressed this in PR #96449 usinggetMemberLoginByOptimisticAccountID, this helper was not exported nor used inWorkspaceMemberDetailsRolePage.tsx.What changes do you think we should make in order to solve the problem?
Export
getMemberLoginByOptimisticAccountIDinsrc/libs/PolicyUtils/index.ts(orsrc/libs/PolicyMemberRoleUtils.ts):Update
src/pages/workspace/members/WorkspaceMemberDetailsRolePage.tsx:getMemberLoginByOptimisticAccountID:resolvedAccountIDfor role updates and back navigation:In
src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx:Import
getMemberLoginByOptimisticAccountIDfrom@libs/PolicyUtilsinstead of inlining it.What alternative solutions did you explore? (Optional)
AddMembersToWorkspacefinishes: Poor UX; optimism is a core principle in New Expensify. Recovering the member login preserves instant responsiveness.What specific scenarios should we cover in automated tests?
In
tests/ui/WorkspaceMemberDetailsRolePageTest.tsx:WorkspaceMemberDetailsRolePagewith an optimisticaccountIDsuccessfully resolves the member frompolicy.employeeListand renders the role list.updateWorkspaceMembersRolewith the resolved ID and navigates back cleanly without errors.marufsharifi commentedon Oct 12, 2026
reproduced, i will check the proposal today.
marufsharifi commentedon Oct 13, 2026
@MelvinBot implement this
MelvinBot commentedon Oct 13, 2026
🤖 Draft PR created: Keep the workspace member Role page working right after inviting a member
Please mark it as "Ready for review" or ask me to do that for you when it is ready for review.
The PR implements the proposal. The Role, Work arrangement, and custom field pages now find the member even when the route still holds the optimistic accountID. A new Role page UI test covers this case.
The automated web smoke test couldn't run because the test browser showed a blank page. Please test it manually before review.
melvin-bot commentedon Oct 16, 2026
@marufsharifi Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!
marufsharifi commentedon Oct 17, 2026
PR still under review