From 4e58a0708106e751b2efb1d53ac7d9a6fcfcb289 Mon Sep 17 00:00:00 2001 From: joseph0926 Date: Fri, 14 Nov 2025 10:33:26 +0900 Subject: [PATCH] fix: handle notFound from params.parse correctly --- packages/router-core/src/router.ts | 12 +++-- packages/router-core/tests/load.test.ts | 60 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/packages/router-core/src/router.ts b/packages/router-core/src/router.ts index 962c536045a..1cd71467300 100644 --- a/packages/router-core/src/router.ts +++ b/packages/router-core/src/router.ts @@ -1379,7 +1379,7 @@ export class RouterCore< const strictParams = existingMatch?._strictParams ?? usedParams - let paramsError: PathParamError | undefined = undefined + let paramsError: unknown = undefined if (!existingMatch) { const strictParseParams = @@ -1392,9 +1392,13 @@ export class RouterCore< strictParseParams(strictParams as Record), ) } catch (err: any) { - paramsError = new PathParamError(err.message, { - cause: err, - }) + if (isNotFound(err) || isRedirect(err)) { + paramsError = err + } else { + paramsError = new PathParamError(err.message, { + cause: err, + }) + } if (opts?.throwOnError) { throw paramsError diff --git a/packages/router-core/tests/load.test.ts b/packages/router-core/tests/load.test.ts index be9c248ac12..d65d37cf747 100644 --- a/packages/router-core/tests/load.test.ts +++ b/packages/router-core/tests/load.test.ts @@ -509,6 +509,66 @@ test('cancelMatches after pending timeout', async () => { expect(cancelledFooMatch?._nonReactive.pendingTimeout).toBeUndefined() }) +describe('params.parse notFound', () => { + test('throws notFound on invalid params', async () => { + const rootRoute = new BaseRootRoute({}) + const testRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/test/$id', + params: { + parse: ({ id }: { id: string }) => { + const parsed = parseInt(id, 10) + if (Number.isNaN(parsed)) { + throw notFound() + } + return { id: parsed } + }, + }, + }) + const routeTree = rootRoute.addChildren([testRoute]) + const router = new RouterCore({ + routeTree, + history: createMemoryHistory({ initialEntries: ['/test/invalid'] }), + }) + + await router.load() + + const match = router.state.pendingMatches?.find( + (m) => m.routeId === testRoute.id, + ) + + expect(match?.status).toBe('notFound') + }) + + test('succeeds on valid params', async () => { + const rootRoute = new BaseRootRoute({}) + const testRoute = new BaseRoute({ + getParentRoute: () => rootRoute, + path: '/test/$id', + params: { + parse: ({ id }: { id: string }) => { + const parsed = parseInt(id, 10) + if (Number.isNaN(parsed)) { + throw notFound() + } + return { id: parsed } + }, + }, + }) + const routeTree = rootRoute.addChildren([testRoute]) + const router = new RouterCore({ + routeTree, + history: createMemoryHistory({ initialEntries: ['/test/123'] }), + }) + + await router.load() + + const match = router.state.matches.find((m) => m.routeId === testRoute.id) + expect(match?.status).toBe('success') + expect(router.state.statusCode).toBe(200) + }) +}) + function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)) }