From 66eb642c56c8bc482b0f3c63fb5332bf28fcf597 Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Sun, 20 Jul 2025 20:42:14 +0900 Subject: [PATCH 1/5] fix(router-core): update onError to accept routerCode parameter Signed-off-by: leesb971204 --- packages/router-core/src/route.ts | 2 +- packages/router-core/src/router.ts | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/router-core/src/route.ts b/packages/router-core/src/route.ts index 5d01b977f0e..b11cea9407c 100644 --- a/packages/router-core/src/route.ts +++ b/packages/router-core/src/route.ts @@ -1072,7 +1072,7 @@ export interface UpdatableRouteOptions< SearchFilter> > onCatch?: (error: Error) => void - onError?: (err: any) => void + onError?: (err: any, routerCode?: string) => 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 30cb77f74a1..43b0d6385a9 100644 --- a/packages/router-core/src/router.ts +++ b/packages/router-core/src/router.ts @@ -2145,7 +2145,7 @@ export class RouterCore< err = this.resolveRedirect(err) throw err } else if (isNotFound(err)) { - this._handleNotFound(matches, err, { + this._handleNotFound(matches, err, undefined, { updateMatch, }) throw err @@ -2187,12 +2187,11 @@ export class RouterCore< throw err } - err.routerCode = routerCode firstBadMatchIndex = firstBadMatchIndex ?? index handleRedirectAndNotFound(this.getMatch(matchId)!, err) try { - route.options.onError?.(err) + route.options.onError?.(err, routerCode) } catch (errorHandlerErr) { err = errorHandlerErr handleRedirectAndNotFound(this.getMatch(matchId)!, err) @@ -3019,6 +3018,7 @@ export class RouterCore< _handleNotFound = ( matches: Array, err: NotFoundError, + routerCode?: string, { updateMatch = this.updateMatch, }: { @@ -3069,10 +3069,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, }) } From e71f4fefd12b0136ff8718ffad1e0223b7295330 Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Sun, 20 Jul 2025 20:54:19 +0900 Subject: [PATCH 2/5] feat(router-core): add RouterCode type Signed-off-by: leesb971204 --- packages/router-core/src/route.ts | 4 ++-- packages/router-core/src/router.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/router-core/src/route.ts b/packages/router-core/src/route.ts index b11cea9407c..63efff39053 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, routerCode?: string) => 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 43b0d6385a9..b613cdc54d6 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 @@ -2175,7 +2177,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]! From 890c7a98a2d1ef46de3b7f9861dfa600757728a6 Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Sun, 20 Jul 2025 21:17:58 +0900 Subject: [PATCH 3/5] fix(router-core): update _handleNotFound to include routerCode parameter Signed-off-by: leesb971204 --- packages/router-core/src/router.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts index b613cdc54d6..928f905012d 100644 --- a/packages/router-core/src/router.ts +++ b/packages/router-core/src/router.ts @@ -2147,7 +2147,7 @@ export class RouterCore< err = this.resolveRedirect(err) throw err } else if (isNotFound(err)) { - this._handleNotFound(matches, err, undefined, { + this._handleNotFound(matches, err, 'BEFORE_LOAD', { updateMatch, }) throw err From cb1a58da449b7fa8768a39a0451764cfa1ab5ac6 Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Mon, 21 Jul 2025 16:47:31 +0900 Subject: [PATCH 4/5] test(router): add test for error handling in beforeLoad with primitive type error Signed-off-by: leesb971204 --- packages/react-router/tests/link.test.tsx | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/packages/react-router/tests/link.test.tsx b/packages/react-router/tests/link.test.tsx index 4a8b15a959b..fab45d8f0f1 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!, From b63d947ea225b3b5173431e481e07a43a65ed670 Mon Sep 17 00:00:00 2001 From: leesb971204 Date: Mon, 21 Jul 2025 17:18:43 +0900 Subject: [PATCH 5/5] fix(router-core): enhance handleRedirectAndNotFound to accept routerCode parameter Signed-off-by: leesb971204 --- packages/router-core/src/router.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts index 928f905012d..8c28e967e2b 100644 --- a/packages/router-core/src/router.ts +++ b/packages/router-core/src/router.ts @@ -2108,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) { @@ -2147,7 +2151,7 @@ export class RouterCore< err = this.resolveRedirect(err) throw err } else if (isNotFound(err)) { - this._handleNotFound(matches, err, 'BEFORE_LOAD', { + this._handleNotFound(matches, err, routerCode, { updateMatch, }) throw err @@ -2190,13 +2194,21 @@ export class RouterCore< } firstBadMatchIndex = firstBadMatchIndex ?? index - handleRedirectAndNotFound(this.getMatch(matchId)!, err) + handleRedirectAndNotFound( + this.getMatch(matchId)!, + err, + routerCode, + ) try { route.options.onError?.(err, routerCode) } catch (errorHandlerErr) { err = errorHandlerErr - handleRedirectAndNotFound(this.getMatch(matchId)!, err) + handleRedirectAndNotFound( + this.getMatch(matchId)!, + err, + routerCode, + ) } updateMatch(matchId, (prev) => {