diff --git a/static/app/views/app/index.tsx b/static/app/views/app/index.tsx
index e0efe2a18e1bda..f616a08b965de1 100644
--- a/static/app/views/app/index.tsx
+++ b/static/app/views/app/index.tsx
@@ -32,6 +32,7 @@ import {useLocation} from 'sentry/utils/useLocation';
import {useUser} from 'sentry/utils/useUser';
import type {InstallWizardProps} from 'sentry/views/admin/installWizard';
import {AsyncSDKIntegrationContextProvider} from 'sentry/views/app/asyncSDKIntegrationProvider';
+import LastKnownRouteContextProvider from 'sentry/views/lastKnownRouteContextProvider';
import {OrganizationContextProvider} from 'sentry/views/organizationContext';
import RouteAnalyticsContextProvider from 'sentry/views/routeAnalyticsContextProvider';
@@ -241,22 +242,24 @@ function App({children, params}: Props) {
return (
-
-
-
-
-
-
-
-
-
- {renderBody()}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {renderBody()}
+
+
+
+
+
+
+
);
}
diff --git a/static/app/views/lastKnownRouteContextProvider.tsx b/static/app/views/lastKnownRouteContextProvider.tsx
new file mode 100644
index 00000000000000..38fae90648a579
--- /dev/null
+++ b/static/app/views/lastKnownRouteContextProvider.tsx
@@ -0,0 +1,31 @@
+import {createContext, useContext} from 'react';
+
+import getRouteStringFromRoutes from 'sentry/utils/getRouteStringFromRoutes';
+import usePrevious from 'sentry/utils/usePrevious';
+import {useRoutes} from 'sentry/utils/useRoutes';
+
+interface Props {
+ children: React.ReactNode;
+}
+
+export const LastKnownRouteContext = createContext('');
+
+export function useLastKnownRoute() {
+ return useContext(LastKnownRouteContext);
+}
+
+/**
+ * This provider tracks the last known route that the user has navigated to.
+ * This is used to better group issues when we hit "route not found" errors.
+ */
+export default function LastKnownRouteContextProvider({children}: Props) {
+ const route = useRoutes();
+ const prevRoute = usePrevious(route);
+ const lastKnownRoute = getRouteStringFromRoutes(prevRoute);
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/static/app/views/routeNotFound.tsx b/static/app/views/routeNotFound.tsx
index e02dde44fbc034..c92f7ca4aecc62 100644
--- a/static/app/views/routeNotFound.tsx
+++ b/static/app/views/routeNotFound.tsx
@@ -8,11 +8,13 @@ import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
import Sidebar from 'sentry/components/sidebar';
import {t} from 'sentry/locale';
import type {RouteComponentProps} from 'sentry/types/legacyReactRouter';
+import {useLastKnownRoute} from 'sentry/views/lastKnownRouteContextProvider';
type Props = RouteComponentProps<{}, {}>;
function RouteNotFound({router, location}: Props) {
const {pathname, search, hash} = location;
+ const lastKnownRoute = useLastKnownRoute();
const isMissingSlash = pathname[pathname.length - 1] !== '/';
@@ -27,13 +29,14 @@ function RouteNotFound({router, location}: Props) {
scope.setFingerprint(['RouteNotFound']);
scope.setTag('isMissingSlash', isMissingSlash);
scope.setTag('pathname', pathname);
+ scope.setTag('lastKnownRoute', lastKnownRoute);
scope.setTag(
'reactRouterVersion',
window.__SENTRY_USING_REACT_ROUTER_SIX ? '6' : '3'
);
Sentry.captureException(new Error('Route not found'));
});
- }, [pathname, search, hash, isMissingSlash, router]);
+ }, [pathname, search, hash, isMissingSlash, router, lastKnownRoute]);
if (isMissingSlash) {
return null;