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
[Due for payment 2026-10-22] [$175] Web - Profile - The error doesn't match the description in the test case. #102917
Verify the user is requested to add the magic code sent to the main account
Navigate to the email inbox
Enter the magic code sent to the main account and click "Verify"
Enter the email B, then click the 'Add' button
Expected Result:
The error should match the description in the test case.
Actual Result:
After step 10, the user sees the following error: “Failed to add this contact method. Please reach out to Concierge for help” instead of the error required by the test case: “Existing accounts cannot be added as Contact Methods…”
The App never contains the "Existing accounts cannot be added as Contact Methods…" text. The server sends it in the AddNewContactMethod response's Onyx data. The App shows it only if it lands in the Onyx key the page reads.
Migrate loginList to logins Onyx key with expensifyLoginsSelector (merged 2026-06-09) moved the contact method page from loginList to logins (1_<email> entries). The page now reads only logins (DynamicNewContactMethodPage.tsx:40-58). Earlier, the server put this error into loginList[email].errorFields.addedLogin. If it still writes the error only there, nothing reads it. The App then shows its own failureData fallback, "Failed to add this contact method. Please reach out to Concierge for help." (User.ts:512).
What changes do you think we should make in order to solve the problem?
The likely fix is on the backend. It needs to write the AddNewContactMethod "existing account" error to logins under the 1_<email> key, with a microsecond error key. The App's lookup would then show the server's message over the generic fallback (ErrorUtils.ts:136-145). No App change should be needed. An internal engineer needs to confirm the server's response shape and make the backend change.
What alternative solutions did you explore? (Optional)
Make the App read loginList again. This would undo the migration, so I don't recommend it.
Show the response.message the server returns. The App only sends that message to tracking (OnyxUpdates.ts:74-99). Showing it would mean new plumbing for one command.
Investigation details
Confidence: medium. The App side is confirmed in code. I could not see the server's response: logs were unavailable, and the web test session failed to load, so I could not reproduce the error.
addNewContactMethod always merges the generic translated error into logins['1_<email>'].errorFields.addedLogin when the request fails (User.ts:441-522). The server's Onyx data is applied first, and the newest error key wins. So when the server writes to the same entry, its message should show.
Note: the help article docs/articles/new-expensify/getting-started/Troubleshoot-Adding-Your-Work-Email.md describes the generic message as current behavior. Update it if the server message is restored.
To confirm: check the AddNewContactMethod response for vupham12990+2@gmail.com on 2026-10-02. If the error appears under loginList and not logins, that confirms this cause.
Next Steps for Contributor+ team: To accept: The fix likely needs a backend change, so an internal engineer should confirm and take this on. @MelvinBot implement [this](https://github.com/Expensify/App/issues/102917) applies only if they confirm an App change is needed. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
changed the title [-]Web - Profile - The error doesn't match the description in the test case.[/-][+][$175] Web - Profile - The error doesn't match the description in the test case.[/+]on Oct 2, 2026
The server does send its specific error, and it writes it where the page reads it. MelvinBot's proposal asks to confirm the AddNewContactMethod response, so I captured it on a local build of main (production API; this issue also reproduces on production) while adding an email that belongs to another account:
logins → "1_<email>" → errorFields.addedLogin → { "1791195551960999": "Cannot add an existing Expensify account as a new contact method. Please merge accounts or add a copilot instead." }
The App side is the problem. The request's failureData adds the App's generic fallback to the same field, keyed with the device clock (Date.now() * 1000):
The generic text only comes from this failureData, so the tester's page had either no server error for that login, or a server error with a smaller key. The capture rules out the first.
The server's key comes from the server clock, the fallback's from the device clock. In my run with a correct clock, the server stamped its error 0.36s after the tap, so its key was larger and its message showed. If the device clock is ahead of the server by more than that gap, the fallback's key is larger and the page shows "Failed to add this contact method. Please reach out to Concierge for help.", as in this issue.
Same account, with the browser clock moved 3s ahead (const n = Date.now; Date.now = () => n() + 3000 in the console, or set the computer clock a few seconds ahead):
Server error key
Fallback key
Shown
Correct clock
1791189008511467
1791189008149000
server message
Clock +3s
1791195551960999
1791195554681000
generic fallback
Clock +3s, with the change below
1791195861813843
0
server message
I can't see the tester's clock, so this is the most likely cause rather than a confirmed one. Checking the test device on https://time.is would confirm it.
What changes do you think we should make in order to solve the problem?
Give the generic fallback the key 0, so any error the server sends always sorts newer, whatever the device clock says. In addNewContactMethod:
This is the same clock-skew fix merged on Sep 25 for the bank account fallback, with the same key 0 and comment (BankAccounts.ts#L556-L557, #101909), and the same key 0 used for the payment fallback in #85154.
Nothing else changes: with no server error, the fallback is the only entry and still shows. optimisticData clears addedLogin on every attempt, the red dot only checks that an error exists, and clearContactMethod removes the whole entry, so none of them depend on the key.
Tests in tests/actions/UserTest.ts: (1) device clock 3s ahead + server error → the server message is returned (fails on main); (2) no server error → the generic fallback is returned.
main, clock +3s
With the change, clock +3s
Note: after the fix, the page shows the server's current text ("Cannot add an existing Expensify account…"). The test case still quotes the older server text seen in #78331 ("Existing accounts cannot be added…"), so its wording needs an update too.
What alternative solutions did you explore? (Optional)
Backend change (MelvinBot's proposal): the captured response shows the server already writes the error to logins['1_<email>'], so the key location isn't the cause. MelvinBot's details also assume the server's key is always the newest, which fails when the device clock is ahead.
Key the fallback with server-adjusted time (getServerAnchoredDBTime / NETWORK.timeSkew): the skew comes from the HTTP Date header (1-second precision) and is only measured on OpenApp/ReconnectApp/OpenReport. Its error can be bigger than the ~0.3s gap it would need to resolve, so it can't guarantee the order. 0 always works.
[Contributor] The offending PR and associated issue have been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.
Link to the comment on the PR: N/A
Link to the comment on the Issue: N/A
[Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.
Link to discussion: N/A
[Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.
Regression Test Proposal
Test:
Precondition: You have two emails that each already have an Expensify account (email A and email B).
Sign in with email A.
Go to Account > Profile > Contact method.
Click "New contact method".
Enter the magic code sent to email A and click "Verify".
Enter email B and click "Add".
Verify the error explains that an existing Expensify account can't be added as a contact method.
Activity
applause-bot commentedon Oct 2, 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/3ca88e2f6d1e4f10054cf18e757af039ee518647211d0fe38a40125e544da9bb
https://platform.applause.com/services/links/v1/external/2031335fb4dfc5daa0f380cb6e16bdbef8835c48abfff11f4a592beb996a9e0e
MelvinBot commentedon Oct 2, 2026
Proposal
What is the root cause of that problem?
The App never contains the "Existing accounts cannot be added as Contact Methods…" text. The server sends it in the
AddNewContactMethodresponse's Onyx data. The App shows it only if it lands in the Onyx key the page reads.Migrate loginList to logins Onyx key with expensifyLoginsSelector (merged 2026-06-09) moved the contact method page from
loginListtologins(1_<email>entries). The page now reads onlylogins(DynamicNewContactMethodPage.tsx:40-58). Earlier, the server put this error intologinList[email].errorFields.addedLogin. If it still writes the error only there, nothing reads it. The App then shows its own failureData fallback, "Failed to add this contact method. Please reach out to Concierge for help." (User.ts:512).What changes do you think we should make in order to solve the problem?
The likely fix is on the backend. It needs to write the
AddNewContactMethod"existing account" error tologinsunder the1_<email>key, with a microsecond error key. The App's lookup would then show the server's message over the generic fallback (ErrorUtils.ts:136-145). No App change should be needed. An internal engineer needs to confirm the server's response shape and make the backend change.What alternative solutions did you explore? (Optional)
loginListagain. This would undo the migration, so I don't recommend it.response.messagethe server returns. The App only sends that message to tracking (OnyxUpdates.ts:74-99). Showing it would mean new plumbing for one command.Investigation details
addNewContactMethodalways merges the generic translated error intologins['1_<email>'].errorFields.addedLoginwhen the request fails (User.ts:441-522). The server's Onyx data is applied first, and the newest error key wins. So when the server writes to the same entry, its message should show.loginList.docs/articles/new-expensify/getting-started/Troubleshoot-Adding-Your-Work-Email.mddescribes the generic message as current behavior. Update it if the server message is restored.AddNewContactMethodresponse for vupham12990+2@gmail.com on 2026-10-02. If the error appears underloginListand notlogins, that confirms this cause.Next Steps for Contributor+ team:
To accept: The fix likely needs a backend change, so an internal engineer should confirm and take this on.
@MelvinBot implement [this](https://github.com/Expensify/App/issues/102917)applies only if they confirm an App change is needed.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
view run · no recording available
melvin-bot commentedon Oct 2, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @DylanDylann (
External)[-]Web - Profile - The error doesn't match the description in the test case.[/-][+][$175] Web - Profile - The error doesn't match the description in the test case.[/+]melvin-bot commentedon Oct 2, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022106111200343672772
laveshfonix commentedon Oct 5, 2026
Proposal
What is the root cause of that problem?
The server does send its specific error, and it writes it where the page reads it. MelvinBot's proposal asks to confirm the
AddNewContactMethodresponse, so I captured it on a local build ofmain(production API; this issue also reproduces on production) while adding an email that belongs to another account:The App side is the problem. The request's
failureDataadds the App's generic fallback to the same field, keyed with the device clock (Date.now() * 1000):App/src/libs/actions/User.ts
Line 512 in d24c34f
The new contact method page (and the contact method details page) show the error with the largest key:
App/src/libs/ErrorUtils.ts
Line 143 in d24c34f
The generic text only comes from this
failureData, so the tester's page had either no server error for that login, or a server error with a smaller key. The capture rules out the first.The server's key comes from the server clock, the fallback's from the device clock. In my run with a correct clock, the server stamped its error 0.36s after the tap, so its key was larger and its message showed. If the device clock is ahead of the server by more than that gap, the fallback's key is larger and the page shows "Failed to add this contact method. Please reach out to Concierge for help.", as in this issue.
Same account, with the browser clock moved 3s ahead (
const n = Date.now; Date.now = () => n() + 3000in the console, or set the computer clock a few seconds ahead):179118900851146717911890081490001791195551960999179119555468100017911958618138430I can't see the tester's clock, so this is the most likely cause rather than a confirmed one. Checking the test device on https://time.is would confirm it.
What changes do you think we should make in order to solve the problem?
Give the generic fallback the key
0, so any error the server sends always sorts newer, whatever the device clock says. InaddNewContactMethod:This is the same clock-skew fix merged on Sep 25 for the bank account fallback, with the same key
0and comment (BankAccounts.ts#L556-L557, #101909), and the same key0used for the payment fallback in #85154.Nothing else changes: with no server error, the fallback is the only entry and still shows.
optimisticDataclearsaddedLoginon every attempt, the red dot only checks that an error exists, andclearContactMethodremoves the whole entry, so none of them depend on the key.Tests in
tests/actions/UserTest.ts: (1) device clock 3s ahead + server error → the server message is returned (fails onmain); (2) no server error → the generic fallback is returned.main, clock +3sNote: after the fix, the page shows the server's current text ("Cannot add an existing Expensify account…"). The test case still quotes the older server text seen in #78331 ("Existing accounts cannot be added…"), so its wording needs an update too.
What alternative solutions did you explore? (Optional)
logins['1_<email>'], so the key location isn't the cause. MelvinBot's details also assume the server's key is always the newest, which fails when the device clock is ahead.getServerAnchoredDBTime/NETWORK.timeSkew): the skew comes from the HTTPDateheader (1-second precision) and is only measured on OpenApp/ReconnectApp/OpenReport. Its error can be bigger than the ~0.3s gap it would need to resolve, so it can't guarantee the order.0always works.getLatestErrorField: a server error and a client fallback are both plain timestamp keys, so the helper can't tell them apart without a marker on every caller. I'd follow the per-flow precedent (Fix: Surface specific payment error from backend instead of generic fallback #85154, [Payment due @daledah] Surface the server's bank account ownership error instead of generic copy #101909) and keep this toaddNewContactMethod. The delete and set-default fallbacks inUser.tscould take the same0if the server sends its own errors there, but notvalidateLogin, which is read withgetEarliestErrorField, so0would always win there.melvin-bot commentedon Oct 5, 2026
@DylanDylann Whoops! This issue is 2 days overdue. Let's get this updated quick!
DylanDylann commentedon Oct 6, 2026
Not overdue. I will review EOD or tomorrow
23 remaining items
DylanDylann commentedon Oct 21, 2026
Contributor+ Checklist:
[Contributor] The offending PR and associated issue have been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.
Link to the comment on the PR: N/A
Link to the comment on the Issue: N/A
[Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.
Link to discussion: N/A
[Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.
Regression Test Proposal
Test:
Precondition: You have two emails that each already have an Expensify account (email A and email B).
Do we agree 👍 or 👎
melvin-bot commentedon Oct 21, 2026
Triggered auto assignment to @mallenexpensify (
Awaiting Payment)melvin-bot commentedon Oct 21, 2026
Payment Summary
Resolving PRs:
Show the server error when adding a contact method fails #103832
Reviewer: @DylanDylann owed $175 via NewDot
BugZero Checklist (@mallenexpensify)
mallenexpensify commentedon Oct 24, 2026
Payment Summary
Contributor+: @DylanDylann due $175 via NewDot