From 443ab2db1ae1a77fc806994b898d7a28daa80ecc Mon Sep 17 00:00:00 2001
From: Yehor Kharchenko <yehor.kharchenko@swmansion.com>
Date: Tue, 3 Feb 2026 14:26:28 +0100
Subject: [PATCH 01/10] add intercept and validate dynamic suffixes

---
 src/ROUTES.ts                                 |  3 +-
 src/libs/Navigation/NavigationRoot.tsx        |  4 +-
 .../helpers/getAdaptedStateFromPath.ts        | 46 +++++++++++--
 .../helpers/getLastSuffixFromPath.ts          | 21 ++++++
 .../helpers/getStateForDynamicRoute.ts        | 67 +++++++++++++++++++
 .../Navigation/helpers/getStateFromPath.ts    | 28 +++++++-
 src/libs/actions/Welcome/OnboardingFlow.ts    |  3 +-
 7 files changed, 161 insertions(+), 11 deletions(-)
 create mode 100644 src/libs/Navigation/helpers/getLastSuffixFromPath.ts
 create mode 100644 src/libs/Navigation/helpers/getStateForDynamicRoute.ts

diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 13f056926a3d..fea0dd60ccb6 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -79,7 +79,8 @@ type DynamicRoutes = Record<string, DynamicRouteConfig>;
  */
 const DYNAMIC_ROUTES = {
     VERIFY_ACCOUNT: {
-        path: 'verify-account',
+        // The path is intentionally misspelled to avoid conflicts when dynamic routes logic isn't entirely ready
+        path: 'verify-accountt',
         entryScreens: [],
     },
 } as const satisfies DynamicRoutes;
diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx
index 7f65fb9dc465..a251aedf6214 100644
--- a/src/libs/Navigation/NavigationRoot.tsx
+++ b/src/libs/Navigation/NavigationRoot.tsx
@@ -109,7 +109,7 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N
                 Navigation.navigate(ROUTES.MIGRATED_USER_WELCOME_MODAL.getRoute());
             });
 
-            return getAdaptedStateFromPath(lastVisitedPath, linkingConfig.config);
+            return getAdaptedStateFromPath(lastVisitedPath);
         }
 
         if (!account || account.isFromPublicDomain) {
@@ -132,7 +132,7 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N
 
             if (!isSpecificDeepLink) {
                 Log.info('Restoring last visited path on app startup', false, {lastVisitedPath, initialUrl, path});
-                return getAdaptedStateFromPath(lastVisitedPath, linkingConfig.config);
+                return getAdaptedStateFromPath(lastVisitedPath);
             }
         }
 
diff --git a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts
index fdae94a668e1..dfbabdad248c 100644
--- a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts
+++ b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts
@@ -1,21 +1,24 @@
-import type {NavigationState, PartialState, Route} from '@react-navigation/native';
-import {findFocusedRoute, getStateFromPath} from '@react-navigation/native';
+import type {NavigationState, PartialState, getStateFromPath as RNGetStateFromPath, Route} from '@react-navigation/native';
+import {findFocusedRoute} from '@react-navigation/native';
 import pick from 'lodash/pick';
 import type {OnyxCollection} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
 import getInitialSplitNavigatorState from '@libs/Navigation/AppNavigator/createSplitNavigator/getInitialSplitNavigatorState';
-import {config} from '@libs/Navigation/linkingConfig/config';
 import {RHP_TO_DOMAIN, RHP_TO_SEARCH, RHP_TO_SETTINGS, RHP_TO_SIDEBAR, RHP_TO_WORKSPACE, RHP_TO_WORKSPACES_LIST} from '@libs/Navigation/linkingConfig/RELATIONS';
 import type {NavigationPartialRoute, RootNavigatorParamList} from '@libs/Navigation/types';
 import CONST from '@src/CONST';
 import NAVIGATORS from '@src/NAVIGATORS';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
+import type {Route as RoutePath} from '@src/ROUTES';
 import SCREENS from '@src/SCREENS';
 import type {Report} from '@src/types/onyx';
+import getLastSuffixFromPath from './getLastSuffixFromPath';
 import getMatchingNewRoute from './getMatchingNewRoute';
 import getParamsFromRoute from './getParamsFromRoute';
 import getRedirectedPath from './getRedirectedPath';
+import getStateFromPath from './getStateFromPath';
+import isDynamicRouteSuffix from './isDynamicRouteSuffix';
 import {isFullScreenName} from './isNavigatorName';
 import normalizePath from './normalizePath';
 import replacePathInNestedState from './replacePathInNestedState';
@@ -31,7 +34,7 @@ Onyx.connect({
 
 type GetAdaptedStateReturnType = ReturnType<typeof getStateFromPath>;
 
-type GetAdaptedStateFromPath = (...args: [...Parameters<typeof getStateFromPath>, shouldReplacePathInNestedState?: boolean]) => GetAdaptedStateReturnType;
+type GetAdaptedStateFromPath = (...args: [...Parameters<typeof RNGetStateFromPath>, shouldReplacePathInNestedState?: boolean]) => GetAdaptedStateReturnType;
 
 // The function getPathFromState that we are using in some places isn't working correctly without defined index.
 const getRoutesWithIndex = (routes: NavigationPartialRoute[]): PartialState<NavigationState> => ({routes, index: routes.length - 1});
@@ -64,7 +67,7 @@ function getSearchScreenNameForRoute(route: NavigationPartialRoute): string {
 function getMatchingFullScreenRoute(route: NavigationPartialRoute) {
     // Check for backTo param. One screen with different backTo value may need different screens visible under the overlay.
     if (isRouteWithBackToParam(route)) {
-        const stateForBackTo = getStateFromPath(route.params.backTo, config);
+        const stateForBackTo = getStateFromPath(route.params.backTo as RoutePath);
 
         // This may happen if the backTo url is invalid.
         const lastRoute = stateForBackTo?.routes.at(-1);
@@ -87,6 +90,37 @@ function getMatchingFullScreenRoute(route: NavigationPartialRoute) {
         // If not, get the matching full screen route for the back to state.
         return getMatchingFullScreenRoute(focusedStateForBackToRoute);
     }
+
+    // Handle dynamic routes: find the appropriate full screen route
+    const dynamicRouteSuffix = getLastSuffixFromPath(route.path);
+    if (isDynamicRouteSuffix(dynamicRouteSuffix)) {
+        // Remove dynamic suffix to get the base path
+        const pathWithoutDynamicSuffix = route.path?.replace(`/${dynamicRouteSuffix}`, '');
+
+        // Get navigation state for the base path without dynamic suffix
+        const stateUnderDynamicRoute = getStateFromPath(pathWithoutDynamicSuffix as RoutePath);
+        const lastRoute = stateUnderDynamicRoute?.routes.at(-1);
+
+        if (!stateUnderDynamicRoute || !lastRoute || lastRoute.name === SCREENS.NOT_FOUND) {
+            return undefined;
+        }
+
+        const isLastRouteFullScreen = isFullScreenName(lastRoute.name);
+
+        if (isLastRouteFullScreen) {
+            return lastRoute;
+        }
+
+        const focusedStateForDynamicRoute = findFocusedRoute(stateUnderDynamicRoute);
+
+        if (!focusedStateForDynamicRoute) {
+            return undefined;
+        }
+
+        // Recursively find the matching full screen route for the focused dynamic route
+        return getMatchingFullScreenRoute(focusedStateForDynamicRoute);
+    }
+
     const routeNameForLookup = getSearchScreenNameForRoute(route);
     if (RHP_TO_SEARCH[routeNameForLookup]) {
         const paramsFromRoute = getParamsFromRoute(RHP_TO_SEARCH[routeNameForLookup]);
@@ -278,7 +312,7 @@ const getAdaptedStateFromPath: GetAdaptedStateFromPath = (path, options, shouldR
         normalizedPath = '/';
     }
 
-    const state = getStateFromPath(normalizedPath, options) as PartialState<NavigationState<RootNavigatorParamList>>;
+    const state = getStateFromPath(normalizedPath as RoutePath) as PartialState<NavigationState<RootNavigatorParamList>>;
     if (shouldReplacePathInNestedState) {
         replacePathInNestedState(state, normalizedPath);
     }
diff --git a/src/libs/Navigation/helpers/getLastSuffixFromPath.ts b/src/libs/Navigation/helpers/getLastSuffixFromPath.ts
new file mode 100644
index 000000000000..3094f923de21
--- /dev/null
+++ b/src/libs/Navigation/helpers/getLastSuffixFromPath.ts
@@ -0,0 +1,21 @@
+/**
+ * Extracts the last segment from a URL path, removing query parameters and trailing slashes.
+ *
+ * @param path - The URL path to extract the suffix from (can be undefined)
+ * @returns The last segment of the path as a string
+ */
+function getLastSuffixFromPath(path: string | undefined): string {
+    const pathWithoutParams = path?.split('?').at(0);
+
+    if (!pathWithoutParams) {
+        throw new Error('Failed to parse the path, path is empty');
+    }
+
+    const pathWithoutTrailingSlash = pathWithoutParams.endsWith('/') ? pathWithoutParams.slice(0, -1) : pathWithoutParams;
+
+    const lastSuffix = pathWithoutTrailingSlash.split('/').pop() ?? '';
+
+    return lastSuffix;
+}
+
+export default getLastSuffixFromPath;
diff --git a/src/libs/Navigation/helpers/getStateForDynamicRoute.ts b/src/libs/Navigation/helpers/getStateForDynamicRoute.ts
new file mode 100644
index 000000000000..60bb53d0e13f
--- /dev/null
+++ b/src/libs/Navigation/helpers/getStateForDynamicRoute.ts
@@ -0,0 +1,67 @@
+import {normalizedConfigs} from '@libs/Navigation/linkingConfig/config';
+import {DYNAMIC_ROUTES} from '@src/ROUTES';
+import type {DynamicRouteSuffix} from '@src/ROUTES';
+
+type LeafRoute = {
+    name: string;
+    path: string;
+};
+
+type NestedRoute = {
+    name: string;
+    state: {
+        routes: [RouteNode];
+        index: 0;
+    };
+};
+
+type RouteNode = LeafRoute | NestedRoute;
+
+function getRouteNamesForDynamicRoute(dynamicRouteName: DynamicRouteSuffix): string[] | null {
+    // Search through normalized configs to find matching path and extract navigation hierarchy
+    // routeNames contains the sequence of screen/navigator names that should be present in the navigation state
+    for (const [, config] of Object.entries(normalizedConfigs)) {
+        if (config.path === dynamicRouteName) {
+            return config.routeNames;
+        }
+    }
+
+    return null;
+}
+
+function getStateForDynamicRoute(path: string, dynamicRouteName: keyof typeof DYNAMIC_ROUTES) {
+    const routeConfig = getRouteNamesForDynamicRoute(DYNAMIC_ROUTES[dynamicRouteName].path);
+
+    if (!routeConfig) {
+        throw new Error(`No route configuration found for dynamic route '${dynamicRouteName}'`);
+    }
+
+    // Build navigation state by creating nested structure
+    const buildNestedState = (routes: string[], currentIndex: number): RouteNode => {
+        const currentRoute = routes.at(currentIndex);
+
+        // If this is the last route, create leaf node with path
+        if (currentIndex === routes.length - 1) {
+            return {
+                name: currentRoute ?? '',
+                path,
+            };
+        }
+
+        // Create intermediate node with nested state
+        return {
+            name: currentRoute ?? '',
+            state: {
+                routes: [buildNestedState(routes, currentIndex + 1)],
+                index: 0,
+            },
+        };
+    };
+
+    // Start building from the first route
+    const rootRoute = {routes: [buildNestedState(routeConfig, 0)]};
+
+    return rootRoute;
+}
+
+export default getStateForDynamicRoute;
diff --git a/src/libs/Navigation/helpers/getStateFromPath.ts b/src/libs/Navigation/helpers/getStateFromPath.ts
index b08d003e3c13..1e5725617c8e 100644
--- a/src/libs/Navigation/helpers/getStateFromPath.ts
+++ b/src/libs/Navigation/helpers/getStateFromPath.ts
@@ -1,9 +1,14 @@
 import type {NavigationState, PartialState} from '@react-navigation/native';
-import {getStateFromPath as RNGetStateFromPath} from '@react-navigation/native';
+import {findFocusedRoute, getStateFromPath as RNGetStateFromPath} from '@react-navigation/native';
 import {linkingConfig} from '@libs/Navigation/linkingConfig';
 import type {Route} from '@src/ROUTES';
+import {DYNAMIC_ROUTES} from '@src/ROUTES';
+import type {Screen} from '@src/SCREENS';
+import getLastSuffixFromPath from './getLastSuffixFromPath';
 import getMatchingNewRoute from './getMatchingNewRoute';
 import getRedirectedPath from './getRedirectedPath';
+import getStateForDynamicRoute from './getStateForDynamicRoute';
+import isDynamicRouteSuffix from './isDynamicRouteSuffix';
 
 /**
  * @param path - The path to parse
@@ -14,6 +19,27 @@ function getStateFromPath(path: Route): PartialState<NavigationState> {
     const redirectedPath = getRedirectedPath(normalizedPath);
     const normalizedPathAfterRedirection = getMatchingNewRoute(redirectedPath) ?? redirectedPath;
 
+    const dynamicRouteSuffix = getLastSuffixFromPath(path);
+    if (isDynamicRouteSuffix(dynamicRouteSuffix)) {
+        const pathWithoutDynamicSuffix = path.replace(`/${dynamicRouteSuffix}`, '');
+
+        type DynamicRouteKey = keyof typeof DYNAMIC_ROUTES;
+
+        // Find the dynamic route key that matches the extracted suffix
+        const dynamicRoute: string = Object.keys(DYNAMIC_ROUTES).find((key) => DYNAMIC_ROUTES[key as DynamicRouteKey].path === dynamicRouteSuffix) ?? '';
+
+        // Get the currently focused route from the base path to check permissions
+        const focusedRoute = findFocusedRoute(getStateFromPath(pathWithoutDynamicSuffix as Route) ?? {});
+        const entryScreens: Screen[] = DYNAMIC_ROUTES[dynamicRoute as DynamicRouteKey]?.entryScreens ?? [];
+
+        // Check if the focused route is allowed to access this dynamic route
+        if (focusedRoute?.name && entryScreens.includes(focusedRoute.name as Screen)) {
+            // Generate navigation state for the dynamic route
+            const verifyAccountState = getStateForDynamicRoute(normalizedPath, dynamicRoute as DynamicRouteKey);
+            return verifyAccountState;
+        }
+    }
+
     // This function is used in the linkTo function where we want to use default getStateFromPath function.
     const state = RNGetStateFromPath(normalizedPathAfterRedirection, linkingConfig.config);
 
diff --git a/src/libs/actions/Welcome/OnboardingFlow.ts b/src/libs/actions/Welcome/OnboardingFlow.ts
index 0a6c01e8adfe..c99144e73600 100644
--- a/src/libs/actions/Welcome/OnboardingFlow.ts
+++ b/src/libs/actions/Welcome/OnboardingFlow.ts
@@ -14,6 +14,7 @@ import IntlStore from '@src/languages/IntlStore';
 import NAVIGATORS from '@src/NAVIGATORS';
 import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
+import type {Route} from '@src/ROUTES';
 import {hasCompletedGuidedSetupFlowSelector} from '@src/selectors/Onboarding';
 import type {Locale, Onboarding} from '@src/types/onyx';
 
@@ -83,7 +84,7 @@ Onyx.connectWithoutView({
  */
 function startOnboardingFlow(startOnboardingFlowParams: GetOnboardingInitialPathParamsType) {
     const currentRoute = navigationRef.getCurrentRoute();
-    const adaptedState = getAdaptedStateFromPath(getOnboardingInitialPath(startOnboardingFlowParams), linkingConfig.config, false);
+    const adaptedState = getAdaptedStateFromPath(getOnboardingInitialPath(startOnboardingFlowParams) as Route, linkingConfig.config, false);
     const focusedRoute = findFocusedRoute(adaptedState as PartialState<NavigationState<RootNavigatorParamList>>);
     if (focusedRoute?.name === currentRoute?.name) {
         return;

From 0ceeb409179fcba1382243fa08f1b88b4d54f853 Mon Sep 17 00:00:00 2001
From: Yehor Kharchenko <yehor.kharchenko@swmansion.com>
Date: Wed, 4 Feb 2026 10:23:31 +0100
Subject: [PATCH 02/10] no need to add misspelled route name, there's no entry
 screens

---
 src/ROUTES.ts | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index fea0dd60ccb6..13f056926a3d 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -79,8 +79,7 @@ type DynamicRoutes = Record<string, DynamicRouteConfig>;
  */
 const DYNAMIC_ROUTES = {
     VERIFY_ACCOUNT: {
-        // The path is intentionally misspelled to avoid conflicts when dynamic routes logic isn't entirely ready
-        path: 'verify-accountt',
+        path: 'verify-account',
         entryScreens: [],
     },
 } as const satisfies DynamicRoutes;

From 4ea27616c22477e342c3ca7b44f36152a2363416 Mon Sep 17 00:00:00 2001
From: Yehor Kharchenko <yehor.kharchenko@swmansion.com>
Date: Wed, 4 Feb 2026 11:18:36 +0100
Subject: [PATCH 03/10] add all neccessary files to create first dynamic page

---
 src/hooks/useDynamicBackPath.ts               | 17 +++++++++
 src/libs/AppState/index.ts                    |  5 +--
 .../ModalStackNavigators/index.tsx            |  1 +
 .../syncBrowserHistory/index.web.ts           |  5 +--
 src/libs/Navigation/Navigation.ts             |  5 ++-
 .../Navigation/helpers/getPathFromState.ts    | 37 +++++++++++++++++++
 .../settings/DynamicVerifyAccountPage.tsx     | 18 +++++++++
 7 files changed, 80 insertions(+), 8 deletions(-)
 create mode 100644 src/hooks/useDynamicBackPath.ts
 create mode 100644 src/libs/Navigation/helpers/getPathFromState.ts
 create mode 100644 src/pages/settings/DynamicVerifyAccountPage.tsx

diff --git a/src/hooks/useDynamicBackPath.ts b/src/hooks/useDynamicBackPath.ts
new file mode 100644
index 000000000000..aafa57b17662
--- /dev/null
+++ b/src/hooks/useDynamicBackPath.ts
@@ -0,0 +1,17 @@
+import {useNavigationState} from '@react-navigation/native';
+import getPathFromState from '@libs/Navigation/helpers/getPathFromState';
+import type {DynamicRouteSuffix, Route} from '@src/ROUTES';
+import ROUTES from '@src/ROUTES';
+
+/**
+ * Returns the back path for a dynamic route by removing the dynamic suffix from the current URL.
+ * @param dynamicRouteSuffix The dynamic route suffix to remove from the current path
+ * @returns The back path without the dynamic route suffix
+ */
+function useDynamicBackPath(dynamicRouteSuffix: DynamicRouteSuffix): Route {
+    const path = useNavigationState((state) => getPathFromState(state));
+    const backPath = path ? (path.replace(`/${dynamicRouteSuffix}`, '') as Route) : ROUTES.HOME;
+    return backPath;
+}
+
+export default useDynamicBackPath;
diff --git a/src/libs/AppState/index.ts b/src/libs/AppState/index.ts
index 38e3d3f574d3..ddf8d4a82ac2 100644
--- a/src/libs/AppState/index.ts
+++ b/src/libs/AppState/index.ts
@@ -1,9 +1,8 @@
-import {getPathFromState} from '@react-navigation/native';
 import type {OnyxEntry} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
 import type {ValueOf} from 'type-fest';
 import Log from '@libs/Log';
-import {linkingConfig} from '@libs/Navigation/linkingConfig';
+import getPathFromState from '@libs/Navigation/helpers/getPathFromState';
 import {navigationRef} from '@libs/Navigation/Navigation';
 import {isAuthenticating as isAuthenticatingNetworkStore} from '@libs/Network/NetworkStore';
 import CONST from '@src/CONST';
@@ -41,7 +40,7 @@ function captureNavigationState(): NavigationStateInfo {
             return {currentPath: undefined};
         }
 
-        const routeFromState = getPathFromState(navigationRef.getRootState(), linkingConfig.config);
+        const routeFromState = getPathFromState(navigationRef.getRootState());
         return {
             currentPath: routeFromState || undefined,
         };
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
index 8e34f4043826..61089f417453 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
@@ -353,6 +353,7 @@ const ConsoleModalStackNavigator = createModalStackNavigator<ConsoleNavigatorPar
 });
 
 const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorParamList>({
+    [SCREENS.SETTINGS.DYNAMIC_VERIFY_ACCOUNT]: () => require<ReactComponentModule>('../../../../pages/settings/DynamicVerifyAccountPage').default,
     [SCREENS.SETTINGS.SHARE_CODE]: () => require<ReactComponentModule>('../../../../pages/ShareCodePage').default,
     [SCREENS.SETTINGS.PROFILE.PRONOUNS]: () => require<ReactComponentModule>('../../../../pages/settings/Profile/PronounsPage').default,
     [SCREENS.SETTINGS.PROFILE.DISPLAY_NAME]: () => require<ReactComponentModule>('../../../../pages/settings/Profile/DisplayNamePage').default,
diff --git a/src/libs/Navigation/AppNavigator/createRootStackNavigator/syncBrowserHistory/index.web.ts b/src/libs/Navigation/AppNavigator/createRootStackNavigator/syncBrowserHistory/index.web.ts
index 77431c8ca14b..a650fb00f8c7 100644
--- a/src/libs/Navigation/AppNavigator/createRootStackNavigator/syncBrowserHistory/index.web.ts
+++ b/src/libs/Navigation/AppNavigator/createRootStackNavigator/syncBrowserHistory/index.web.ts
@@ -1,11 +1,10 @@
 import type {ParamListBase, StackNavigationState} from '@react-navigation/native';
-import {getPathFromState} from '@react-navigation/native';
-import {linkingConfig} from '@libs/Navigation/linkingConfig';
+import getPathFromState from '@libs/Navigation/helpers/getPathFromState';
 
 function syncBrowserHistory(state: StackNavigationState<ParamListBase>) {
     // We reset the URL as the browser sets it in a way that doesn't match the navigation state
     // eslint-disable-next-line no-restricted-globals
-    history.replaceState({}, '', getPathFromState(state, linkingConfig.config));
+    history.replaceState({}, '', getPathFromState(state));
 }
 
 export default syncBrowserHistory;
diff --git a/src/libs/Navigation/Navigation.ts b/src/libs/Navigation/Navigation.ts
index 35c74372fcf5..9f00beae8507 100644
--- a/src/libs/Navigation/Navigation.ts
+++ b/src/libs/Navigation/Navigation.ts
@@ -1,6 +1,6 @@
 import {findFocusedRoute, getActionFromState} from '@react-navigation/core';
 import type {EventArg, NavigationAction, NavigationContainerEventMap, NavigationState, PartialState} from '@react-navigation/native';
-import {CommonActions, getPathFromState, StackActions} from '@react-navigation/native';
+import {CommonActions, StackActions} from '@react-navigation/native';
 import {Str} from 'expensify-common';
 // eslint-disable-next-line you-dont-need-lodash-underscore/omit
 import omit from 'lodash/omit';
@@ -25,6 +25,7 @@ import type {Account, SidePanel} from '@src/types/onyx';
 import getInitialSplitNavigatorState from './AppNavigator/createSplitNavigator/getInitialSplitNavigatorState';
 import getSearchTopmostReportParams from './getSearchTopmostReportParams';
 import originalCloseRHPFlow from './helpers/closeRHPFlow';
+import getPathFromState from './helpers/getPathFromState';
 import getStateFromPath from './helpers/getStateFromPath';
 import getTopmostReportParams from './helpers/getTopmostReportParams';
 import {isFullScreenName, isOnboardingFlowName, isSplitNavigatorName} from './helpers/isNavigatorName';
@@ -212,7 +213,7 @@ function getActiveRoute(): string {
         return '';
     }
 
-    const routeFromState = getPathFromState(navigationRef.getRootState(), linkingConfig.config);
+    const routeFromState = getPathFromState(navigationRef.getRootState());
 
     if (routeFromState) {
         return routeFromState;
diff --git a/src/libs/Navigation/helpers/getPathFromState.ts b/src/libs/Navigation/helpers/getPathFromState.ts
new file mode 100644
index 000000000000..7718dbfdc405
--- /dev/null
+++ b/src/libs/Navigation/helpers/getPathFromState.ts
@@ -0,0 +1,37 @@
+import {findFocusedRoute, getPathFromState as RNGetPathFromState} from '@react-navigation/native';
+import type {NavigationState, PartialState} from '@react-navigation/routers';
+import {linkingConfig} from '@libs/Navigation/linkingConfig';
+import {normalizedConfigs} from '@libs/Navigation/linkingConfig/config';
+import {DYNAMIC_ROUTES} from '@src/ROUTES';
+import type {Screen} from '@src/SCREENS';
+
+type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
+
+/**
+ * Checks if a screen name is a dynamic route screen
+ */
+function isDynamicRouteScreen(screenName: Screen): boolean {
+    for (const {path} of Object.values(DYNAMIC_ROUTES)) {
+        if (normalizedConfigs[screenName]?.path === path) {
+            return true;
+        }
+    }
+    return false;
+}
+
+const getPathFromState = (state: State): string => {
+    const focusedRoute = findFocusedRoute(state);
+    const screenName = focusedRoute?.name ?? '';
+
+    // Handle dynamic route screens that require special path that is placed in state
+    if (isDynamicRouteScreen(screenName as Screen) && focusedRoute?.path) {
+        return focusedRoute.path;
+    }
+
+    // For regular routes, use React Navigation's default path generation
+    const path = RNGetPathFromState(state, linkingConfig.config);
+
+    return path;
+};
+
+export default getPathFromState;
diff --git a/src/pages/settings/DynamicVerifyAccountPage.tsx b/src/pages/settings/DynamicVerifyAccountPage.tsx
new file mode 100644
index 000000000000..d842e3c44c46
--- /dev/null
+++ b/src/pages/settings/DynamicVerifyAccountPage.tsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import useDynamicBackPath from '@hooks/useDynamicBackPath';
+import {DYNAMIC_ROUTES} from '@src/ROUTES';
+import VerifyAccountPageBase from './VerifyAccountPageBase';
+
+function DynamicVerifyAccountPage() {
+    const backPath = useDynamicBackPath(DYNAMIC_ROUTES.VERIFY_ACCOUNT.path);
+    // currently, the default behavior of this component after completing verification is to navigate back
+    const forwardPath = backPath;
+    return (
+        <VerifyAccountPageBase
+            navigateBackTo={backPath}
+            navigateForwardTo={forwardPath}
+        />
+    );
+}
+
+export default DynamicVerifyAccountPage;

From 1a2c199de5db08dcf3237f5dadafdfcdd1f6c4b3 Mon Sep 17 00:00:00 2001
From: Yehor Kharchenko <yehor.kharchenko@swmansion.com>
Date: Wed, 4 Feb 2026 13:36:30 +0100
Subject: [PATCH 04/10] fix test

---
 tests/navigation/isActiveRouteTests.tsx | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/tests/navigation/isActiveRouteTests.tsx b/tests/navigation/isActiveRouteTests.tsx
index 0e88faa69b58..a592eb7f6d33 100644
--- a/tests/navigation/isActiveRouteTests.tsx
+++ b/tests/navigation/isActiveRouteTests.tsx
@@ -1,5 +1,5 @@
 import {afterEach, beforeEach, describe, expect, it, jest} from '@jest/globals';
-import type {getPathFromState as GetPathFromState} from '@react-navigation/native';
+import getPathFromState from '@libs/Navigation/helpers/getPathFromState';
 import Navigation from '@libs/Navigation/Navigation';
 import navigationRef from '@libs/Navigation/navigationRef';
 import type {Route} from '@src/ROUTES';
@@ -18,14 +18,7 @@ jest.mock('@libs/Navigation/navigationRef', () => {
     };
 });
 
-jest.mock('@react-navigation/native', () => {
-    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
-    const actual = jest.requireActual('@react-navigation/native') as {getPathFromState: typeof GetPathFromState};
-    return {
-        ...actual,
-        getPathFromState: jest.fn<typeof GetPathFromState>(() => '/settings/profile?backTo=settings'),
-    };
-});
+jest.mock('@libs/Navigation/helpers/getPathFromState', () => jest.fn());
 
 describe('Navigation', () => {
     afterEach(() => {
@@ -39,8 +32,10 @@ describe('Navigation', () => {
             isReady: jest.Mock;
         };
 
+        (getPathFromState as jest.Mock).mockReturnValue('/settings/profile?backTo=settings');
+
         navigationRefMock.current.getCurrentRoute.mockReturnValue({name: 'test'});
-        navigationRefMock.getRootState.mockReturnValue({} as ReturnType<typeof navigationRef.getRootState>);
+        navigationRefMock.getRootState.mockReturnValue({});
         navigationRefMock.isReady.mockReturnValue(true);
     });
 

From e1c6fa7bfa41eca8122ca797f8d288311247fecd Mon Sep 17 00:00:00 2001
From: Yehor Kharchenko <yehor.kharchenko@swmansion.com>
Date: Fri, 6 Feb 2026 15:28:00 +0100
Subject: [PATCH 05/10] add a guard to prevent path = undefined

---
 .../helpers/getAdaptedStateFromPath.ts        | 44 ++++++++++---------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts
index dfbabdad248c..b64b96f66c91 100644
--- a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts
+++ b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts
@@ -92,33 +92,35 @@ function getMatchingFullScreenRoute(route: NavigationPartialRoute) {
     }
 
     // Handle dynamic routes: find the appropriate full screen route
-    const dynamicRouteSuffix = getLastSuffixFromPath(route.path);
-    if (isDynamicRouteSuffix(dynamicRouteSuffix)) {
-        // Remove dynamic suffix to get the base path
-        const pathWithoutDynamicSuffix = route.path?.replace(`/${dynamicRouteSuffix}`, '');
-
-        // Get navigation state for the base path without dynamic suffix
-        const stateUnderDynamicRoute = getStateFromPath(pathWithoutDynamicSuffix as RoutePath);
-        const lastRoute = stateUnderDynamicRoute?.routes.at(-1);
+    if (route.path) {
+        const dynamicRouteSuffix = getLastSuffixFromPath(route.path);
+        if (isDynamicRouteSuffix(dynamicRouteSuffix)) {
+            // Remove dynamic suffix to get the base path
+            const pathWithoutDynamicSuffix = route.path?.replace(`/${dynamicRouteSuffix}`, '');
+
+            // Get navigation state for the base path without dynamic suffix
+            const stateUnderDynamicRoute = getStateFromPath(pathWithoutDynamicSuffix as RoutePath);
+            const lastRoute = stateUnderDynamicRoute?.routes.at(-1);
+
+            if (!stateUnderDynamicRoute || !lastRoute || lastRoute.name === SCREENS.NOT_FOUND) {
+                return undefined;
+            }
 
-        if (!stateUnderDynamicRoute || !lastRoute || lastRoute.name === SCREENS.NOT_FOUND) {
-            return undefined;
-        }
+            const isLastRouteFullScreen = isFullScreenName(lastRoute.name);
 
-        const isLastRouteFullScreen = isFullScreenName(lastRoute.name);
+            if (isLastRouteFullScreen) {
+                return lastRoute;
+            }
 
-        if (isLastRouteFullScreen) {
-            return lastRoute;
-        }
+            const focusedStateForDynamicRoute = findFocusedRoute(stateUnderDynamicRoute);
 
-        const focusedStateForDynamicRoute = findFocusedRoute(stateUnderDynamicRoute);
+            if (!focusedStateForDynamicRoute) {
+                return undefined;
+            }
 
-        if (!focusedStateForDynamicRoute) {
-            return undefined;
+            // Recursively find the matching full screen route for the focused dynamic route
+            return getMatchingFullScreenRoute(focusedStateForDynamicRoute);
         }
-
-        // Recursively find the matching full screen route for the focused dynamic route
-        return getMatchingFullScreenRoute(focusedStateForDynamicRoute);
     }
 
     const routeNameForLookup = getSearchScreenNameForRoute(route);

From 74175863fa05acf9db306f00ea0857c254295a2a Mon Sep 17 00:00:00 2001
From: Yehor Kharchenko <yehor.kharchenko@swmansion.com>
Date: Fri, 6 Feb 2026 15:48:54 +0100
Subject: [PATCH 06/10] add filename to the error text

---
 src/libs/Navigation/helpers/getLastSuffixFromPath.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/Navigation/helpers/getLastSuffixFromPath.ts b/src/libs/Navigation/helpers/getLastSuffixFromPath.ts
index 3094f923de21..1178acbf8539 100644
--- a/src/libs/Navigation/helpers/getLastSuffixFromPath.ts
+++ b/src/libs/Navigation/helpers/getLastSuffixFromPath.ts
@@ -8,7 +8,7 @@ function getLastSuffixFromPath(path: string | undefined): string {
     const pathWithoutParams = path?.split('?').at(0);
 
     if (!pathWithoutParams) {
-        throw new Error('Failed to parse the path, path is empty');
+        throw new Error('[getLastSuffixFromPath.ts] Failed to parse the path, path is empty');
     }
 
     const pathWithoutTrailingSlash = pathWithoutParams.endsWith('/') ? pathWithoutParams.slice(0, -1) : pathWithoutParams;

From d2822ca48e8322c38ecebf99d765bc67febccad7 Mon Sep 17 00:00:00 2001
From: Yehor Kharchenko <yehor.kharchenko@swmansion.com>
Date: Tue, 10 Feb 2026 17:14:26 +0100
Subject: [PATCH 07/10] add and fix all suggestions left by bots

---
 src/hooks/useDynamicBackPath.ts               | 25 +++++++++++++++---
 .../Navigation/helpers/createDynamicRoute.ts  | 15 ++++++++---
 .../Navigation/helpers/getPathFromState.ts    | 11 ++++++--
 .../helpers/getStateForDynamicRoute.ts        |  4 ++-
 .../Navigation/helpers/getStateFromPath.ts    | 26 ++++++++++++++++---
 .../Navigation/helpers/splitPathAndQuery.ts   | 13 ++++++++++
 6 files changed, 80 insertions(+), 14 deletions(-)
 create mode 100644 src/libs/Navigation/helpers/splitPathAndQuery.ts

diff --git a/src/hooks/useDynamicBackPath.ts b/src/hooks/useDynamicBackPath.ts
index aafa57b17662..ffdfcb26ea58 100644
--- a/src/hooks/useDynamicBackPath.ts
+++ b/src/hooks/useDynamicBackPath.ts
@@ -1,17 +1,36 @@
 import {useNavigationState} from '@react-navigation/native';
 import getPathFromState from '@libs/Navigation/helpers/getPathFromState';
+import splitPathAndQuery from '@libs/Navigation/helpers/splitPathAndQuery';
 import type {DynamicRouteSuffix, Route} from '@src/ROUTES';
 import ROUTES from '@src/ROUTES';
 
 /**
  * Returns the back path for a dynamic route by removing the dynamic suffix from the current URL.
+ * Only removes the suffix if it's the last segment of the path (ignoring trailing slashes and query parameters).
  * @param dynamicRouteSuffix The dynamic route suffix to remove from the current path
- * @returns The back path without the dynamic route suffix
+ * @returns The back path without the dynamic route suffix, or HOME if path is null/undefined
  */
 function useDynamicBackPath(dynamicRouteSuffix: DynamicRouteSuffix): Route {
     const path = useNavigationState((state) => getPathFromState(state));
-    const backPath = path ? (path.replace(`/${dynamicRouteSuffix}`, '') as Route) : ROUTES.HOME;
-    return backPath;
+
+    if (!path) {
+        return ROUTES.HOME;
+    }
+
+    // Remove leading slashes for consistent processing
+    const pathWithoutLeadingSlash = path.replace(/^\/+/, '');
+
+    const [normalizedPath, query] = splitPathAndQuery(pathWithoutLeadingSlash);
+
+    if (normalizedPath?.endsWith(`/${dynamicRouteSuffix}`)) {
+        const backPathWithoutQuery = normalizedPath.slice(0, -(dynamicRouteSuffix.length + 1));
+        const backPath = `${backPathWithoutQuery}${query ? `?${query}` : ''}`;
+
+        return backPath as Route;
+    }
+
+    // If suffix is not the last segment, return the original path
+    return pathWithoutLeadingSlash as Route;
 }
 
 export default useDynamicBackPath;
diff --git a/src/libs/Navigation/helpers/createDynamicRoute.ts b/src/libs/Navigation/helpers/createDynamicRoute.ts
index 903d82f5143c..0b15a35b7376 100644
--- a/src/libs/Navigation/helpers/createDynamicRoute.ts
+++ b/src/libs/Navigation/helpers/createDynamicRoute.ts
@@ -1,13 +1,20 @@
 import Navigation from '@libs/Navigation/Navigation';
 import type {DynamicRouteSuffix, Route} from '@src/ROUTES';
 import isDynamicRouteSuffix from './isDynamicRouteSuffix';
+import splitPathAndQuery from './splitPathAndQuery';
 
 const combinePathAndSuffix = (path: string, suffix: string): Route => {
-    const [basePath, params] = path.split('?');
-    let newPath = basePath.endsWith('/') ? `${basePath}${suffix}` : `${basePath}/${suffix}`;
+    const [normalizedPath, query] = splitPathAndQuery(path);
 
-    if (params) {
-        newPath += `?${params}`;
+    // This should never happen as the path should always be defined
+    if (!normalizedPath) {
+        throw new Error('Path is undefined or empty');
+    }
+
+    let newPath = `${normalizedPath}/${suffix}`;
+
+    if (query) {
+        newPath += `?${query}`;
     }
     return newPath as Route;
 };
diff --git a/src/libs/Navigation/helpers/getPathFromState.ts b/src/libs/Navigation/helpers/getPathFromState.ts
index 7718dbfdc405..4c8b7219e170 100644
--- a/src/libs/Navigation/helpers/getPathFromState.ts
+++ b/src/libs/Navigation/helpers/getPathFromState.ts
@@ -11,8 +11,15 @@ type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
  * Checks if a screen name is a dynamic route screen
  */
 function isDynamicRouteScreen(screenName: Screen): boolean {
-    for (const {path} of Object.values(DYNAMIC_ROUTES)) {
-        if (normalizedConfigs[screenName]?.path === path) {
+    const dynamicRouteEntries = Object.values(DYNAMIC_ROUTES);
+    const screenPath = normalizedConfigs[screenName]?.path;
+
+    if (!screenPath) {
+        return false;
+    }
+
+    for (const {path} of dynamicRouteEntries) {
+        if (screenPath === path) {
             return true;
         }
     }
diff --git a/src/libs/Navigation/helpers/getStateForDynamicRoute.ts b/src/libs/Navigation/helpers/getStateForDynamicRoute.ts
index 60bb53d0e13f..eab4689f041a 100644
--- a/src/libs/Navigation/helpers/getStateForDynamicRoute.ts
+++ b/src/libs/Navigation/helpers/getStateForDynamicRoute.ts
@@ -18,9 +18,11 @@ type NestedRoute = {
 type RouteNode = LeafRoute | NestedRoute;
 
 function getRouteNamesForDynamicRoute(dynamicRouteName: DynamicRouteSuffix): string[] | null {
+    const configEntries = Object.entries(normalizedConfigs);
+
     // Search through normalized configs to find matching path and extract navigation hierarchy
     // routeNames contains the sequence of screen/navigator names that should be present in the navigation state
-    for (const [, config] of Object.entries(normalizedConfigs)) {
+    for (const [, config] of configEntries) {
         if (config.path === dynamicRouteName) {
             return config.routeNames;
         }
diff --git a/src/libs/Navigation/helpers/getStateFromPath.ts b/src/libs/Navigation/helpers/getStateFromPath.ts
index 1e5725617c8e..5bf0ae2d18ba 100644
--- a/src/libs/Navigation/helpers/getStateFromPath.ts
+++ b/src/libs/Navigation/helpers/getStateFromPath.ts
@@ -1,5 +1,6 @@
 import type {NavigationState, PartialState} from '@react-navigation/native';
 import {findFocusedRoute, getStateFromPath as RNGetStateFromPath} from '@react-navigation/native';
+import Log from '@libs/Log';
 import {linkingConfig} from '@libs/Navigation/linkingConfig';
 import type {Route} from '@src/ROUTES';
 import {DYNAMIC_ROUTES} from '@src/ROUTES';
@@ -33,10 +34,27 @@ function getStateFromPath(path: Route): PartialState<NavigationState> {
         const entryScreens: Screen[] = DYNAMIC_ROUTES[dynamicRoute as DynamicRouteKey]?.entryScreens ?? [];
 
         // Check if the focused route is allowed to access this dynamic route
-        if (focusedRoute?.name && entryScreens.includes(focusedRoute.name as Screen)) {
-            // Generate navigation state for the dynamic route
-            const verifyAccountState = getStateForDynamicRoute(normalizedPath, dynamicRoute as DynamicRouteKey);
-            return verifyAccountState;
+        if (focusedRoute?.name) {
+            if (entryScreens.includes(focusedRoute.name as Screen)) {
+                // Generate navigation state for the dynamic route
+                const verifyAccountState = getStateForDynamicRoute(normalizedPath, dynamicRoute as DynamicRouteKey);
+                return verifyAccountState;
+            }
+
+            // Fallback to root parsing so users can't land on /verify-account directly.
+            // This ensures navigation redirects back to the previous screen (root handles that).
+            if (pathWithoutDynamicSuffix === '/' || pathWithoutDynamicSuffix === '') {
+                const state = RNGetStateFromPath(pathWithoutDynamicSuffix, linkingConfig.config);
+
+                if (!state) {
+                    throw new Error('Failed to parse the path to a navigation state.');
+                }
+
+                return state;
+            }
+
+            // Log an error to quickly identify and add forgotten screens to the Dynamic Routes configuration
+            Log.warn(`[getStateFromPath.ts][DynamicRoute] Focused route ${focusedRoute.name} is not allowed to access dynamic route with suffix ${dynamicRouteSuffix}`);
         }
     }
 
diff --git a/src/libs/Navigation/helpers/splitPathAndQuery.ts b/src/libs/Navigation/helpers/splitPathAndQuery.ts
new file mode 100644
index 000000000000..9c87238b8391
--- /dev/null
+++ b/src/libs/Navigation/helpers/splitPathAndQuery.ts
@@ -0,0 +1,13 @@
+/**
+ * Splits a full path into its path and query components.
+ * @param fullPath - The full URL path (e.g., '/settings/wallet?param=value')
+ * @returns A tuple where the first element is the path without trailing slash
+ *          and the second element is the query string (if any).
+ */
+function splitPathAndQuery(fullPath: string | undefined): [string | undefined, string | undefined] {
+    const [path, query] = fullPath?.split('?') ?? [undefined, undefined];
+    const normalizedPath = path?.endsWith('/') && path.length > 1 ? path.slice(0, -1) : path;
+    return [normalizedPath, query];
+}
+
+export default splitPathAndQuery;

From 52f74d39187dcb455bd9e8c1a4c0e736360e0c61 Mon Sep 17 00:00:00 2001
From: Yehor Kharchenko <yehor.kharchenko@swmansion.com>
Date: Wed, 11 Feb 2026 10:22:01 +0100
Subject: [PATCH 08/10] fix getStateFromPath handling dynamic routes and change
 imports for getPathFromState

---
 src/libs/Navigation/NavigationRoot.tsx         |  5 +++--
 .../Navigation/helpers/getStateFromPath.ts     | 18 ++++++------------
 src/libs/actions/App.ts                        |  5 ++---
 3 files changed, 11 insertions(+), 17 deletions(-)

diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx
index 452ef3301ea4..8a56092104b2 100644
--- a/src/libs/Navigation/NavigationRoot.tsx
+++ b/src/libs/Navigation/NavigationRoot.tsx
@@ -1,5 +1,5 @@
 import type {NavigationState} from '@react-navigation/native';
-import {DarkTheme, DefaultTheme, findFocusedRoute, getPathFromState, NavigationContainer} from '@react-navigation/native';
+import {DarkTheme, DefaultTheme, findFocusedRoute, NavigationContainer} from '@react-navigation/native';
 import {hasCompletedGuidedSetupFlowSelector, wasInvitedToNewDotSelector} from '@selectors/Onboarding';
 import React, {useCallback, useContext, useEffect, useMemo, useRef} from 'react';
 import {useOnboardingValues} from '@components/OnyxListItemProvider';
@@ -29,6 +29,7 @@ import ROUTES from '@src/ROUTES';
 import AppNavigator from './AppNavigator';
 import {cleanPreservedNavigatorStates} from './AppNavigator/createSplitNavigator/usePreserveNavigatorState';
 import getAdaptedStateFromPath from './helpers/getAdaptedStateFromPath';
+import getPathFromState from './helpers/getPathFromState';
 import {isWorkspacesTabScreenName} from './helpers/isNavigatorName';
 import {saveSettingsTabPathToSessionStorage, saveWorkspacesTabPathToSessionStorage} from './helpers/lastVisitedTabPathUtils';
 import {linkingConfig} from './linkingConfig';
@@ -56,7 +57,7 @@ function parseAndLogRoute(state: NavigationState) {
         return;
     }
 
-    const currentPath = getPathFromState(state, linkingConfig.config);
+    const currentPath = getPathFromState(state);
 
     const focusedRoute = findFocusedRoute(state);
 
diff --git a/src/libs/Navigation/helpers/getStateFromPath.ts b/src/libs/Navigation/helpers/getStateFromPath.ts
index 5bf0ae2d18ba..ca011426ec17 100644
--- a/src/libs/Navigation/helpers/getStateFromPath.ts
+++ b/src/libs/Navigation/helpers/getStateFromPath.ts
@@ -20,9 +20,9 @@ function getStateFromPath(path: Route): PartialState<NavigationState> {
     const redirectedPath = getRedirectedPath(normalizedPath);
     const normalizedPathAfterRedirection = getMatchingNewRoute(redirectedPath) ?? redirectedPath;
 
-    const dynamicRouteSuffix = getLastSuffixFromPath(path);
+    const dynamicRouteSuffix = getLastSuffixFromPath(normalizedPathAfterRedirection);
     if (isDynamicRouteSuffix(dynamicRouteSuffix)) {
-        const pathWithoutDynamicSuffix = path.replace(`/${dynamicRouteSuffix}`, '');
+        const pathWithoutDynamicSuffix = normalizedPathAfterRedirection.replace(`/${dynamicRouteSuffix}`, '');
 
         type DynamicRouteKey = keyof typeof DYNAMIC_ROUTES;
 
@@ -37,18 +37,13 @@ function getStateFromPath(path: Route): PartialState<NavigationState> {
         if (focusedRoute?.name) {
             if (entryScreens.includes(focusedRoute.name as Screen)) {
                 // Generate navigation state for the dynamic route
-                const verifyAccountState = getStateForDynamicRoute(normalizedPath, dynamicRoute as DynamicRouteKey);
-                return verifyAccountState;
+                const dynamicRouteState = getStateForDynamicRoute(normalizedPath, dynamicRoute as DynamicRouteKey);
+                return dynamicRouteState;
             }
 
-            // Fallback to root parsing so users can't land on /verify-account directly.
-            // This ensures navigation redirects back to the previous screen (root handles that).
+            // Fallback to not found page so users can't land on dynamic suffix directly.
             if (pathWithoutDynamicSuffix === '/' || pathWithoutDynamicSuffix === '') {
-                const state = RNGetStateFromPath(pathWithoutDynamicSuffix, linkingConfig.config);
-
-                if (!state) {
-                    throw new Error('Failed to parse the path to a navigation state.');
-                }
+                const state = {routes: [{name: 'not-found', path: normalizedPathAfterRedirection}]};
 
                 return state;
             }
@@ -64,7 +59,6 @@ function getStateFromPath(path: Route): PartialState<NavigationState> {
     if (!state) {
         throw new Error('Failed to parse the path to a navigation state.');
     }
-
     return state;
 }
 
diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts
index 8b64dde19dae..2a93c758e917 100644
--- a/src/libs/actions/App.ts
+++ b/src/libs/actions/App.ts
@@ -1,5 +1,4 @@
 // Issue - https://github.com/Expensify/App/issues/26719
-import {getPathFromState} from '@react-navigation/native';
 import {Str} from 'expensify-common';
 import type {AppStateStatus} from 'react-native';
 import {AppState} from 'react-native';
@@ -11,7 +10,6 @@ import {SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
 import DateUtils from '@libs/DateUtils';
 import Log from '@libs/Log';
 import getCurrentUrl from '@libs/Navigation/currentUrl';
-import {linkingConfig} from '@libs/Navigation/linkingConfig';
 import Navigation, {navigationRef} from '@libs/Navigation/Navigation';
 import Performance from '@libs/Performance';
 import {isPublicRoom, isValidReport} from '@libs/ReportUtils';
@@ -19,6 +17,7 @@ import {isLoggingInAsNewUser as isLoggingInAsNewUserSessionUtils} from '@libs/Se
 import {clearSoundAssetsCache} from '@libs/Sound';
 import {cancelAllSpans, endSpan, getSpan, startSpan} from '@libs/telemetry/activeSpans';
 import CONST from '@src/CONST';
+import getPathFromState from '@src/libs/Navigation/helpers/getPathFromState';
 import type {OnyxKey} from '@src/ONYXKEYS';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type {Route} from '@src/ROUTES';
@@ -242,7 +241,7 @@ function saveCurrentPathBeforeBackground() {
             return;
         }
 
-        const currentPath = getPathFromState(currentState, linkingConfig.config);
+        const currentPath = getPathFromState(currentState);
 
         if (currentPath) {
             Log.info('Saving current path before background', false, {currentPath});

From 0ec218a48b05817cd971b6dfb54a5690bd52da22 Mon Sep 17 00:00:00 2001
From: Yehor Kharchenko <yehor.kharchenko@swmansion.com>
Date: Wed, 11 Feb 2026 10:50:05 +0100
Subject: [PATCH 09/10] add comment that expains why we should left options arg
 and remove options from args when calling getAdaptedStateFromPath

---
 src/libs/Navigation/NavigationRoot.tsx                 | 1 -
 src/libs/Navigation/helpers/getAdaptedStateFromPath.ts | 5 +++--
 src/libs/actions/Welcome/OnboardingFlow.ts             | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/libs/Navigation/NavigationRoot.tsx b/src/libs/Navigation/NavigationRoot.tsx
index 8a56092104b2..294baabc1924 100644
--- a/src/libs/Navigation/NavigationRoot.tsx
+++ b/src/libs/Navigation/NavigationRoot.tsx
@@ -149,7 +149,6 @@ function NavigationRoot({authenticated, lastVisitedPath, initialUrl, onReady}: N
                     onboardingInitialPath,
                     onboardingValues,
                 }),
-                linkingConfig.config,
             );
         }
 
diff --git a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts
index 3c1761993f6a..396911a1d12e 100644
--- a/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts
+++ b/src/libs/Navigation/helpers/getAdaptedStateFromPath.ts
@@ -304,11 +304,12 @@ function getAdaptedState(state: PartialState<NavigationState<RootNavigatorParamL
  * see the NAVIGATION.md documentation.
  *
  * @param path - The path to generate state from
- * @param options - Extra options to fine-tune how to parse the path
- * @param shouldReplacePathInNestedState - Whether to replace the path in nested state
+ * @param options - Extra options kept for react-navigation compatibility
+ * @param shouldReplacePathInNestedState - Whether to replace the path in nested state (if passing this arg, pass `undefined` for `options`, otherwise omit both)
  * @returns The adapted navigation state
  * @throws Error if unable to get state from path
  */
+// We keep `options` in the signature for `linkingConfig` compatibility with react-navigation.
 const getAdaptedStateFromPath: GetAdaptedStateFromPath = (path, options, shouldReplacePathInNestedState = true) => {
     let normalizedPath = !path.startsWith('/') ? `/${path}` : path;
     normalizedPath = getRedirectedPath(normalizedPath);
diff --git a/src/libs/actions/Welcome/OnboardingFlow.ts b/src/libs/actions/Welcome/OnboardingFlow.ts
index 797982c9b464..1723ead6cd33 100644
--- a/src/libs/actions/Welcome/OnboardingFlow.ts
+++ b/src/libs/actions/Welcome/OnboardingFlow.ts
@@ -83,7 +83,7 @@ Onyx.connectWithoutView({
  */
 function startOnboardingFlow(startOnboardingFlowParams: GetOnboardingInitialPathParamsType) {
     const currentRoute = navigationRef.getCurrentRoute();
-    const adaptedState = getAdaptedStateFromPath(getOnboardingInitialPath(startOnboardingFlowParams) as Route, linkingConfig.config, false);
+    const adaptedState = getAdaptedStateFromPath(getOnboardingInitialPath(startOnboardingFlowParams) as Route, undefined, false);
     const focusedRoute = findFocusedRoute(adaptedState as PartialState<NavigationState<RootNavigatorParamList>>);
     if (focusedRoute?.name === currentRoute?.name) {
         return;

From 1735d03dc8170acfc55cda2d1068b764a66d563a Mon Sep 17 00:00:00 2001
From: Yehor Kharchenko <yehor.kharchenko@swmansion.com>
Date: Wed, 11 Feb 2026 12:39:44 +0100
Subject: [PATCH 10/10] change string screen name to const from SCREENS

---
 src/libs/Navigation/helpers/getStateFromPath.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/libs/Navigation/helpers/getStateFromPath.ts b/src/libs/Navigation/helpers/getStateFromPath.ts
index ca011426ec17..75dd252b3c2f 100644
--- a/src/libs/Navigation/helpers/getStateFromPath.ts
+++ b/src/libs/Navigation/helpers/getStateFromPath.ts
@@ -5,6 +5,7 @@ import {linkingConfig} from '@libs/Navigation/linkingConfig';
 import type {Route} from '@src/ROUTES';
 import {DYNAMIC_ROUTES} from '@src/ROUTES';
 import type {Screen} from '@src/SCREENS';
+import SCREENS from '@src/SCREENS';
 import getLastSuffixFromPath from './getLastSuffixFromPath';
 import getMatchingNewRoute from './getMatchingNewRoute';
 import getRedirectedPath from './getRedirectedPath';
@@ -43,7 +44,7 @@ function getStateFromPath(path: Route): PartialState<NavigationState> {
 
             // Fallback to not found page so users can't land on dynamic suffix directly.
             if (pathWithoutDynamicSuffix === '/' || pathWithoutDynamicSuffix === '') {
-                const state = {routes: [{name: 'not-found', path: normalizedPathAfterRedirection}]};
+                const state = {routes: [{name: SCREENS.NOT_FOUND, path: normalizedPathAfterRedirection}]};
 
                 return state;
             }
