Repository navigation
[Due for payment 2026-10-05] Copilot switch race fires OpenApp with no authToken, landing users on "Hmm... it's not here" #101940
Description
Problem
When a user copilots into another account on New Expensify, the app can land on the "Hmm... it's not here" empty state (or get signed out with "Your session has expired") instead of the delegate's account.
The copilot switch itself succeeds. The failure is a race in NewDot that fires an OpenApp with no authToken, which the API rejects as an expired session.
Observed sequence
From a reported case (times relative to the switch):
ConnectAsDelegatesucceeds, thenOpenAppas the delegate succeeds — the copilot session is live.- ~55s later, a queued
ReconnectAppis canceled mid-transition (signal is aborted without reason), so itsfinallyDatanever clearsisLoadingApp. - Milliseconds later, the recovery hook logs
[Onyx] isLoadingApp missing after app is readyand fires its ownopenApp(). - Two
OpenAppcalls go out with noauthTokenat all. The API returnsMissing authToken→AuthTokenExpired, "Your session has expired." - On retry, the app logs
[Delegate] Missing delegate field after switch, thenRedirecting to Sign In because signOut() was called.
Root cause (proposed)
src/libs/actions/Delegate.ts—connect()swaps the auth token, clears Onyx, then callsopenApp().src/hooks/useIsLoadingAppRecovery.ts:38-49— whenisLoadingAppis left stale (because the in-flightReconnectAppwas aborted by the delegate switch), the recovery hook fires its ownopenApp().
If that recovery openApp() lands inside the delegate-switch window, the request is built before the delegate token is installed, so it goes out with no authToken.
Scope
Not specific to one account. [Delegate] Missing delegate field after switch fires roughly 1,000–1,400 times a day across all users (about 7,800 occurrences over the week of 2026-09-10 to 2026-09-17), so this is a general copilot-switch race.
Fix direction — open question
Two views so far:
- Guard the recovery path: have
Delegate.connect()mark the transition souseIsLoadingAppRecoveryskips it, and make sure the abortedReconnectAppstill clearsisLoadingApp. - Counterpoint (
adhorodyski): that reads as a patch over a real gap. Credentials should already be resolved by this point and delegate access is designed to be sorted out earlier. Skipping inuseIsLoadingAppRecoverylimits the blast radius but may not be the real fix.
Existing work in flight:
- Make the copilot sign-out path readable from the logs (draft — delegate-access telemetry, needs a new owner)
- Stop the IS_LOADING_APP recovery from re-opening the app while a sign-in or reauthentication is in flight (draft — not yet validated)
Possibly related issues
- [Exploratory] Copilot - Not here page when click Create expense in delegate mode
- [Due for payment 2026-09-16] [$250] Co-pilot access gets stuck loading / receipts missing when opening co-worker files
- [$250] Copilot-User is logged out after remove copilot then kill app and open again
- [Due for payment 2026-05-12] [$250] Fix doubled OpenApp on delegate (copilot) account switch (closed)
- [Due for payment 2026-05-12] [$250] Don't fire duplicate ReconnectApp on delegate account switch (closed)
Mirrored from an internal issue so it is publicly visible to contributors and experts. Account identifiers and internal log references were intentionally left out.
Activity
adhorodyski commentedon Sep 23, 2026
I have also explored callstack-internal#70 that could be retargeted on this main.
VickyStash commentedon Sep 23, 2026
Hi there, I'm helping @adhorodyski here 👋
VickyStash commentedon Sep 23, 2026
Update:
I've re-watched the FS sessions of the affected user and noticed one thing:
User A tries to co-pilot to access another account B-> From account B sees
Not Foundpage -> and then tries to access back account A using the transition link from classic and lands back to account A successfully.FS sessions: switching + not found -> noth found + user doing transition to retun to account A -> sucessfully landed on account A (and then all over again).
After a closer look, it turned out the Hmm... it's not here page is not caused by the OpenApp 407 / missing
authToken(these logs are coming from user transitioning user triggered in his own).The root cause of Not found page comes from MigratedUserWelcomeModalGuard!
When the copilot switch finishes loading, the guard proactively navigates to the migrated user welcome modal by appending the
migrated-user-welcomedynamic suffix to the current route. The user was on/settings/wallet, which is not in the modal'sentryScreens, sogetStateFromPathresolved the path toNOT_FOUND.App/src/ROUTES.ts
Lines 251 to 253 in bf1662b
VL logs also proves it:
I've tried locally to force co-pilot to show migrated welcome modal, and got the same Not Found page
not-found-example.mp4
I've just found that there is a PR #100859 that will change it (cc @chrispader). This PR checks whether the active screen can host their dynamic modal. If it cannot, the helper falls back to Home instead of constructing an invalid route. Which means if co-pilot should show the migrated modal, the user will be re-navigated to the Home page and the modal will be shown.
@brianlee-expensify @mountiny But should the migrated user modal be shown while copilot access at all? If so, then the fix above should be enough. If no, I can update the check to not show the migrated user modal for copilot access.
adhorodyski commentedon Sep 23, 2026
#100859 alone will result in this case in being transitioned to Home + having a modal shown
adhorodyski commentedon Sep 23, 2026
I cleared up the drafts and think #101882 would be a nice telemetry followup to distinguish different sign out causes in logs. It'd skip me a lot of rounds here. @brianlee-expensify should I link to this issue too and open for review?
brianlee-expensify commentedon Sep 23, 2026
@VickyStash @adhorodyski will defer to @mountiny for your two questions above!
@adhorodyski shall we close out this GH for now?
adhorodyski commentedon Sep 23, 2026
Yes, let's track this here only.
mountiny commentedon Sep 23, 2026
@adhorodyski @VickyStash great digging!
I think that the migrated modal does not have to show for the copiloted flow
VickyStash commentedon Sep 24, 2026
Here is the PR: #102124
6 remaining items
[-]Copilot switch race fires OpenApp with no authToken, landing users on "Hmm... it's not here"[/-][+][Due for payment 2026-10-05] Copilot switch race fires OpenApp with no authToken, landing users on "Hmm... it's not here"[/+]melvin-bot commentedon Sep 28, 2026
@suneox
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.4.94-0 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
If no regressions arise, payment will be issued on 2026-10-05. 🎊
The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the Contributor+ Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]. If no checklist is needed for this issue, you can click: [no checklist button]
melvin-bot commentedon Oct 1, 2026
@suneox Whoops! This issue is 2 days overdue. Let's get this updated quick!
melvin-bot commentedon Oct 4, 2026
Triggered auto assignment to @mallenexpensify (
Awaiting Payment)melvin-bot commentedon Oct 4, 2026
Payment Summary
Resolving PRs:
Block migrated user welcome modal in copilot access #102124
Reviewer: @suneox owed $175 via NewDot
Contributor: @VickyStash does not require payment (Contractor)
Contributor: @adhorodyski does not require payment (Contractor)
BugZero Checklist (@mallenexpensify)
suneox commentedon Oct 5, 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.
The offending PR could not found, it's an edge case when migrate dynamic route that is difficult to reproduce with a specific data
[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.
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.
N/A: It isn't easy to test for this bug per this comment
mallenexpensify commentedon Oct 7, 2026
Payment Summary
Contributor+: @suneox due $175 via NewDot