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
Chat - Compose box not displayed after logging in via chat link #89028
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
Identify the pull request that introduced this issue and revert it.
Classification: Frontend bug Causing PR: #88733 - "[CP Staging] fix Deeplink - App is stuck on splash screen when navigating to a public room as anon user" by @situchan (Medium confidence) Related Issues: #88653, #88545
Recommendation: NEEDS INVESTIGATION
PR #88733 is the most likely candidate, but the exact mechanism by which it suppresses the compose box after login is not conclusively traced. The PR author and reviewers should verify whether reverting or patching #88733 resolves the issue.
Assigned: @situchan (PR author), @ikevin127@mollfpr (approving reviewers) from highest-confidence causing PR only Labels: Both DeployBlockerCash and DeployBlocker are kept — this is a staging-only regression on a frontend component (App repo), so DeployBlockerCash should remain. DeployBlocker label was already present and is kept pending clarification.
Detailed Analysis
Evidence
Bug description: After logging in via a chat link on Android/iOS native apps, the compose box is not displayed. The user lands on the correct report screen and can see messages, but cannot compose new ones.
Version affected: 9.3.62-5 (staging only, not reproducible in production 9.3.61-x).
Primary suspect — PR #88733: This cherry-pick modifies src/DeepLinkHandler.tsx in two ways:
Adds allReports / allReportsMetadata to the Onyx loading check:
// Before:
if (isLoadingOnyxValue(sessionMetadata, isSelfTourViewedMetadata)) { return; }
// After:
if (isLoadingOnyxValue(allReportsMetadata, sessionMetadata, conciergeReportIDMetadata,
introSelectedMetadata, isSelfTourViewedMetadata, betasMetadata)) {
return;
}
Adds allReportsMetadata.status (and four other new metadata statuses) to the useEffect dependency array.
Why this matters for the login-via-link flow: Before PR #88733, the DeepLinkHandler effect ran when session and self-tour data were ready. After PR #88733, the effect also re-runs whenever allReportsMetadata.status changes. When a user logs in, reports begin loading — allReportsMetadata.status transitions from "not loaded" → "loading" → "loaded". Each transition re-runs the effect, calling Linking.getInitialURL() and potentially openReportFromDeepLink again.
Critically, PR #88733 does NOT include the initialUrlProcessed guard that was added later in PR #86660. Without that guard, openReportFromDeepLink can be called multiple times after the initial URL has already been processed, creating a race condition in the navigation and post-login state setup.
Compose box mechanics: On mobile (isSmallScreenWidth = true), ReportFooter renders the compose box only when !shouldHideComposer && shouldShowComposeInput. The shouldShowComposeInput Onyx key defaults to false in ReportFooter. It is set to true by ReportFetchHandler on mount (InteractionManager.runAfterInteractions). If the re-triggered deep link navigation causes a second navigation event that interrupts or races with ReportFetchHandler's mount lifecycle, the setShouldShowComposeInput(true) call could be cancelled (the interactionTask.cancel() in the cleanup) before it fires.
Relationship to PR #86660: The later main-branch PR #86660 adds comprehensive fixes to DeepLinkHandler.tsx including an initialUrlProcessed ref guard, a Promise.race with timeout (to handle HybridApp bridge timeouts), and a safety-net useEffect for authenticated users. These fixes address the same class of re-invocation issues. PR #88733 (the cherry-pick) only includes the allReportsMetadata change without the protective guards.
Verification
Affected file: src/DeepLinkHandler.tsx — confirmed modified by PR #88733.
Files relevant to compose box:
src/pages/inbox/report/ReportFooter.tsx — controls compose box rendering based on shouldShowComposeInput and canPerformWriteAction
src/pages/inbox/ReportFetchHandler.tsx — sets shouldShowComposeInput(true) on mount
Chronological order confirms PR #88733 is in the 9.3.62-5 build (entry 149 in the StagingDeployCash chronological list, before entry 163 which is the 9.3.62-5 submodule update).
Root Cause
The DeepLinkHandler effect, with allReportsMetadata.status added to its dependency array but without an initialUrlProcessed guard, re-invokes openReportFromDeepLink multiple times as reports load after login. This likely creates a race condition where a second navigation event fires while ReportFetchHandler is setting up, causing the InteractionManager.runAfterInteractions task that calls setShouldShowComposeInput(true) to be cancelled by the cleanup function before it executes. The compose box then renders with shouldShowComposeInput = false and is hidden on mobile.
The complete fix (including the initialUrlProcessed guard and Promise.race timeout) exists in PR #86660 on main but was not cherry-picked to staging alongside PR #88733.
Recommended action: Either revert PR #88733 from staging, or cherry-pick the additional protections from PR #86660 (initialUrlProcessed guard + cancellation logic) into a new staging CP. The PR author @situchan and reviewer @ikevin127 are best positioned to advise on the preferred approach.
@deetergp the original bug is not reproducible anymore. But while executing repro steps in OP, found this navigation issue:
Log in in the app
Copy a conversation/chat link in the note
Log out from the app.
Open a conversation/chat link in step 2.
Log in to the app with same account in step 1 .
Observe the chat screen.
In Step 5, I got console error.
The action 'NAVIGATE' with payload {"name":"TabNavigator","params":{"initial":false,"screen":"ReportsSplitNavigator","params":{"pop":true,"initial":true,"screen":"Report","params":{"reportID":"4147074334554922"},"path":"/r/4147074334554922"},"pop":true},"pop":true} was not handled by any navigator.
Do you have a screen named 'TabNavigator'?
If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.
Yes, this is worth fixing. It's a real navigation timing bug, not just a cosmetic console warning.
Root cause: During the deep link login flow, openReportFromDeepLink is called with isAuthenticated = false. It awaits waitForProtectedRoutes(), which resolves when CONCIERGE appears in routeNames. However, TabNavigator is loaded lazily via getComponent, so there's a window where the RootStack has registered the screen name but the TabNavigator component hasn't fully mounted to handle the nested navigation action. The Navigation.navigate() call at Link.ts:353 dispatches a NAVIGATE action targeting TabNavigator during this window, producing the unhandled action error.
Recommended fix: Add NAVIGATORS.TAB_NAVIGATOR to the PROTECTED_SCREENS check (or add a separate waitForProtectedRoutes check that also verifies TabNavigator is in the navigation state). This ensures waitForProtectedRoutes() doesn't resolve until TabNavigator is actually available to handle the navigation action.
However, since this is a separate bug from the original compose box issue (and the original is no longer reproducible), I'd recommend filing it as a new issue to keep things clean.
The specific TabNavigator NAVIGATE not handled console error from situchan's comment doesn't have its own dedicated issue yet. If you'd like, I can file a new one for it — reply with @MelvinBot file a new issue for the TabNavigator navigation error and I'll create it.
Activity
melvin-bot commentedon Apr 28, 2026
Triggered auto assignment to @deetergp (
DeployBlockerCash), see https://stackoverflowteams.com/c/expensify/questions/9980/ for more details.melvin-bot commentedon Apr 28, 2026
💬 A slack conversation has been started in #expensify-open-source
github-actions commentedon Apr 28, 2026
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
OSBotify commentedon Apr 28, 2026
Investigation Summary
Classification: Frontend bug
Causing PR: #88733 - "[CP Staging] fix Deeplink - App is stuck on splash screen when navigating to a public room as anon user" by @situchan (Medium confidence)
Related Issues: #88653, #88545
Recommendation: NEEDS INVESTIGATION
PR #88733 is the most likely candidate, but the exact mechanism by which it suppresses the compose box after login is not conclusively traced. The PR author and reviewers should verify whether reverting or patching #88733 resolves the issue.
Assigned: @situchan (PR author), @ikevin127 @mollfpr (approving reviewers) from highest-confidence causing PR only
Labels: Both
DeployBlockerCashandDeployBlockerare kept — this is a staging-only regression on a frontend component (App repo), soDeployBlockerCashshould remain.DeployBlockerlabel was already present and is kept pending clarification.Detailed Analysis
Evidence
Bug description: After logging in via a chat link on Android/iOS native apps, the compose box is not displayed. The user lands on the correct report screen and can see messages, but cannot compose new ones.
Version affected: 9.3.62-5 (staging only, not reproducible in production 9.3.61-x).
Primary suspect — PR #88733: This cherry-pick modifies
src/DeepLinkHandler.tsxin two ways:Adds
allReports/allReportsMetadatato the Onyx loading check:Adds
allReportsMetadata.status(and four other new metadata statuses) to theuseEffectdependency array.Why this matters for the login-via-link flow: Before PR #88733, the
DeepLinkHandlereffect ran when session and self-tour data were ready. After PR #88733, the effect also re-runs wheneverallReportsMetadata.statuschanges. When a user logs in, reports begin loading —allReportsMetadata.statustransitions from "not loaded" → "loading" → "loaded". Each transition re-runs the effect, callingLinking.getInitialURL()and potentiallyopenReportFromDeepLinkagain.Critically, PR #88733 does NOT include the
initialUrlProcessedguard that was added later in PR #86660. Without that guard,openReportFromDeepLinkcan be called multiple times after the initial URL has already been processed, creating a race condition in the navigation and post-login state setup.Compose box mechanics: On mobile (
isSmallScreenWidth = true),ReportFooterrenders the compose box only when!shouldHideComposer && shouldShowComposeInput. TheshouldShowComposeInputOnyx key defaults tofalseinReportFooter. It is set totruebyReportFetchHandleron mount (InteractionManager.runAfterInteractions). If the re-triggered deep link navigation causes a second navigation event that interrupts or races withReportFetchHandler's mount lifecycle, thesetShouldShowComposeInput(true)call could be cancelled (theinteractionTask.cancel()in the cleanup) before it fires.Relationship to PR #86660: The later main-branch PR #86660 adds comprehensive fixes to
DeepLinkHandler.tsxincluding aninitialUrlProcessedref guard, aPromise.racewith timeout (to handle HybridApp bridge timeouts), and a safety-netuseEffectfor authenticated users. These fixes address the same class of re-invocation issues. PR #88733 (the cherry-pick) only includes theallReportsMetadatachange without the protective guards.Verification
Affected file:
src/DeepLinkHandler.tsx— confirmed modified by PR #88733.Files relevant to compose box:
src/pages/inbox/report/ReportFooter.tsx— controls compose box rendering based onshouldShowComposeInputandcanPerformWriteActionsrc/pages/inbox/ReportFetchHandler.tsx— setsshouldShowComposeInput(true)on mountsrc/libs/actions/Composer.ts—setShouldShowComposeInputimplementationsrc/ONYXKEYS.ts—SHOULD_SHOW_COMPOSE_INPUTkey definitionChronological order confirms PR #88733 is in the 9.3.62-5 build (entry 149 in the StagingDeployCash chronological list, before entry 163 which is the 9.3.62-5 submodule update).
Root Cause
The
DeepLinkHandlereffect, withallReportsMetadata.statusadded to its dependency array but without aninitialUrlProcessedguard, re-invokesopenReportFromDeepLinkmultiple times as reports load after login. This likely creates a race condition where a second navigation event fires whileReportFetchHandleris setting up, causing theInteractionManager.runAfterInteractionstask that callssetShouldShowComposeInput(true)to be cancelled by the cleanup function before it executes. The compose box then renders withshouldShowComposeInput = falseand is hidden on mobile.The complete fix (including the
initialUrlProcessedguard andPromise.racetimeout) exists in PR #86660 on main but was not cherry-picked to staging alongside PR #88733.Recommended action: Either revert PR #88733 from staging, or cherry-pick the additional protections from PR #86660 (
initialUrlProcessedguard + cancellation logic) into a new staging CP. The PR author @situchan and reviewer @ikevin127 are best positioned to advise on the preferred approach.35 remaining items
situchan commentedon May 20, 2026
@deetergp the original bug is not reproducible anymore. But while executing repro steps in OP, found this navigation issue:
In Step 5, I got console error.
Should we fix this?
MelvinBot commentedon May 20, 2026
Yes, this is worth fixing. It's a real navigation timing bug, not just a cosmetic console warning.
Root cause: During the deep link login flow,
openReportFromDeepLinkis called withisAuthenticated = false. It awaitswaitForProtectedRoutes(), which resolves whenCONCIERGEappears inrouteNames. However,TabNavigatoris loaded lazily viagetComponent, so there's a window where the RootStack has registered the screen name but theTabNavigatorcomponent hasn't fully mounted to handle the nested navigation action. TheNavigation.navigate()call at Link.ts:353 dispatches aNAVIGATEaction targetingTabNavigatorduring this window, producing the unhandled action error.Recommended fix: Add
NAVIGATORS.TAB_NAVIGATORto thePROTECTED_SCREENScheck (or add a separatewaitForProtectedRoutescheck that also verifiesTabNavigatoris in the navigation state). This ensureswaitForProtectedRoutes()doesn't resolve untilTabNavigatoris actually available to handle the navigation action.However, since this is a separate bug from the original compose box issue (and the original is no longer reproducible), I'd recommend filing it as a new issue to keep things clean.
deetergp commentedon May 20, 2026
@MelvinBot Are there currently any open issues that refer to this bug?
MelvinBot commentedon May 20, 2026
I found a few open issues related to deep link navigation failures during/after login, though none report this exact
TabNavigatorconsole error:TabNavigatornot being readyThe specific
TabNavigator NAVIGATE not handledconsole error fromsituchan's comment doesn't have its own dedicated issue yet. If you'd like, I can file a new one for it — reply with@MelvinBot file a new issue for the TabNavigator navigation errorand I'll create it.melvin-bot commentedon May 26, 2026
@deetergp, @mollfpr, @ikevin127, @situchan Huh... This is 4 days overdue. Who can take care of this?
deetergp commentedon May 27, 2026
@MelvinBot file a new issue for the TabNavigator navigation error
MelvinBot commentedon May 27, 2026
🤖 Filed new issue for the TabNavigator navigation error: #91777
situchan commentedon May 27, 2026
@deetergp Can I get assigned in #91777 since I have context as a reporter?
deetergp commentedon May 27, 2026
You got it! Add you, @situchan.
deetergp commentedon May 28, 2026
Since this issue was fixed elsewhere and the error messages are being handled in #91777, I think we can close this one out.