diff --git a/packages/react-router/tests/link.test.tsx b/packages/react-router/tests/link.test.tsx
index 4a8b15a959..fab45d8f0f 100644
--- a/packages/react-router/tests/link.test.tsx
+++ b/packages/react-router/tests/link.test.tsx
@@ -1184,6 +1184,55 @@ describe('Link', () => {
expect(onError).toHaveBeenCalledOnce()
})
+ test('when navigating to /posts with a beforeLoad that throws an primitive type error', async () => {
+ const onError = vi.fn()
+ const rootRoute = createRootRoute()
+ const indexRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ path: '/',
+ component: () => {
+ return (
+ <>
+
Index
+ Posts
+ >
+ )
+ },
+ })
+
+ const PostsComponent = () => {
+ return Posts
+ }
+
+ const postsRoute = createRoute({
+ getParentRoute: () => rootRoute,
+ path: 'posts',
+ beforeLoad: () => {
+ throw 'This is a string error'
+ },
+ onError,
+ errorComponent: () => Oops! Something went wrong!,
+ component: PostsComponent,
+ })
+
+ const router = createRouter({
+ context: { userId: 'userId' },
+ routeTree: rootRoute.addChildren([indexRoute, postsRoute]),
+ history,
+ })
+
+ render()
+
+ const postsLink = await screen.findByRole('link', { name: 'Posts' })
+
+ await act(() => fireEvent.click(postsLink))
+
+ const errorText = await screen.findByText('Oops! Something went wrong!')
+ expect(errorText).toBeInTheDocument()
+
+ expect(onError).toHaveBeenCalledOnce()
+ })
+
test('when navigating to /posts with a beforeLoad that throws an error bubbles to the root', async () => {
const rootRoute = createRootRoute({
errorComponent: () => Oops! Something went wrong!,
diff --git a/packages/router-core/src/route.ts b/packages/router-core/src/route.ts
index 5d01b977f0..63efff3905 100644
--- a/packages/router-core/src/route.ts
+++ b/packages/router-core/src/route.ts
@@ -15,7 +15,7 @@ import type {
} from './Matches'
import type { RootRouteId } from './root'
import type { ParseRoute, RouteById, RoutePaths } from './routeInfo'
-import type { AnyRouter, RegisteredRouter } from './router'
+import type { AnyRouter, RegisteredRouter, RouterCode } from './router'
import type { BuildLocationFn, NavigateFn } from './RouterProvider'
import type {
Assign,
@@ -1072,7 +1072,7 @@ export interface UpdatableRouteOptions<
SearchFilter>
>
onCatch?: (error: Error) => void
- onError?: (err: any) => void
+ onError?: (err: any, routerCode?: RouterCode) => void
// These functions are called as route matches are loaded, stick around and leave the active
// matches
onEnter?: (
diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts
index 30cb77f74a..8c28e967e2 100644
--- a/packages/router-core/src/router.ts
+++ b/packages/router-core/src/router.ts
@@ -679,6 +679,8 @@ export type AnyRouterWithContext = RouterCore<
export type AnyRouter = RouterCore
+export type RouterCode = 'BEFORE_LOAD' | 'PARSE_PARAMS' | 'VALIDATE_SEARCH'
+
export interface ViewTransitionOptions {
types:
| Array
@@ -2106,7 +2108,11 @@ export class RouterCore<
triggerOnReady()
}
- const handleRedirectAndNotFound = (match: AnyRouteMatch, err: any) => {
+ const handleRedirectAndNotFound = (
+ match: AnyRouteMatch,
+ err: any,
+ routerCode?: RouterCode,
+ ) => {
if (isRedirect(err) || isNotFound(err)) {
if (isRedirect(err)) {
if (err.redirectHandled) {
@@ -2145,7 +2151,7 @@ export class RouterCore<
err = this.resolveRedirect(err)
throw err
} else if (isNotFound(err)) {
- this._handleNotFound(matches, err, {
+ this._handleNotFound(matches, err, routerCode, {
updateMatch,
})
throw err
@@ -2175,7 +2181,7 @@ export class RouterCore<
const handleSerialError = (
index: number,
err: any,
- routerCode: string,
+ routerCode: RouterCode,
) => {
const { id: matchId, routeId } = matches[index]!
const route = this.looseRoutesById[routeId]!
@@ -2187,15 +2193,22 @@ export class RouterCore<
throw err
}
- err.routerCode = routerCode
firstBadMatchIndex = firstBadMatchIndex ?? index
- handleRedirectAndNotFound(this.getMatch(matchId)!, err)
+ handleRedirectAndNotFound(
+ this.getMatch(matchId)!,
+ err,
+ routerCode,
+ )
try {
- route.options.onError?.(err)
+ route.options.onError?.(err, routerCode)
} catch (errorHandlerErr) {
err = errorHandlerErr
- handleRedirectAndNotFound(this.getMatch(matchId)!, err)
+ handleRedirectAndNotFound(
+ this.getMatch(matchId)!,
+ err,
+ routerCode,
+ )
}
updateMatch(matchId, (prev) => {
@@ -3019,6 +3032,7 @@ export class RouterCore<
_handleNotFound = (
matches: Array,
err: NotFoundError,
+ routerCode?: string,
{
updateMatch = this.updateMatch,
}: {
@@ -3069,10 +3083,9 @@ export class RouterCore<
error: err,
isFetching: false,
}))
-
- if ((err as any).routerCode === 'BEFORE_LOAD' && routeCursor.parentRoute) {
+ if (routerCode === 'BEFORE_LOAD' && routeCursor.parentRoute) {
err.routeId = routeCursor.parentRoute.id
- this._handleNotFound(matches, err, {
+ this._handleNotFound(matches, err, routerCode, {
updateMatch,
})
}