From 6299cddf2c85aa6e04d1445e32a997e7dda08f44 Mon Sep 17 00:00:00 2001 From: LeSingh1 Date: Thu, 11 Jun 2026 23:38:11 -0700 Subject: [PATCH] fix(react-router): re-export PathParamError from framework packages PathParamError was exported from @tanstack/router-core but missing from the re-export lists in @tanstack/react-router, @tanstack/solid-router, and @tanstack/vue-router. Users who needed to detect path-param validation failures via `error instanceof PathParamError` were forced to import from the transitive dependency @tanstack/router-core directly. Adds PathParamError alongside the existing SearchParamError export in all three framework packages, and adds a test in react-router to verify that a failing params.parse callback surfaces as a PathParamError caught by the route's errorComponent. Fixes #7545 --- packages/react-router/src/index.tsx | 2 +- packages/react-router/tests/router.test.tsx | 34 +++++++++++++++++++++ packages/solid-router/src/index.tsx | 2 +- packages/vue-router/src/index.tsx | 2 +- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/packages/react-router/src/index.tsx b/packages/react-router/src/index.tsx index 6637c7542b..a6fa97219b 100644 --- a/packages/react-router/src/index.tsx +++ b/packages/react-router/src/index.tsx @@ -275,7 +275,7 @@ export type { export { createRouter, Router } from './router' -export { lazyFn, SearchParamError } from '@tanstack/router-core' +export { lazyFn, SearchParamError, PathParamError } from '@tanstack/router-core' export { RouterProvider, RouterContextProvider } from './RouterProvider' export type { RouterProps } from './RouterProvider' diff --git a/packages/react-router/tests/router.test.tsx b/packages/react-router/tests/router.test.tsx index cf3b1c11fe..bc9df3f59c 100644 --- a/packages/react-router/tests/router.test.tsx +++ b/packages/react-router/tests/router.test.tsx @@ -12,6 +12,7 @@ import { composeRewrites, notFound } from '@tanstack/router-core' import { Link, Outlet, + PathParamError, RouterProvider, SearchParamError, createBrowserHistory, @@ -1892,6 +1893,39 @@ describe('search params in URL', () => { }) }) }) + + describe('validates path params', () => { + it('wraps a failing params.parse error in PathParamError', async () => { + let errorSpy: Error | undefined + const rootRoute = createRootRoute() + const postRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/posts/$id', + params: { + parse: ({ id }: { id: string }) => { + if (Number.isNaN(Number(id))) { + throw new Error('id must be a number') + } + return { id: Number(id) } + }, + stringify: ({ id }: { id: number }) => ({ id: String(id) }), + }, + errorComponent: ({ error }) => { + errorSpy = error + return null + }, + }) + const routeTree = rootRoute.addChildren([postRoute]) + const history = createMemoryHistory({ + initialEntries: ['/posts/not-a-number'], + }) + const router = createRouter({ routeTree, history }) + render() + await act(() => router.load()) + + expect(errorSpy).toBeInstanceOf(PathParamError) + }) + }) }) describe('route ids should be consistent after rebuilding the route tree', () => { diff --git a/packages/solid-router/src/index.tsx b/packages/solid-router/src/index.tsx index 31be74086f..fa3b695936 100644 --- a/packages/solid-router/src/index.tsx +++ b/packages/solid-router/src/index.tsx @@ -277,7 +277,7 @@ export type { export { createRouter, Router } from './router' -export { lazyFn, SearchParamError } from '@tanstack/router-core' +export { lazyFn, SearchParamError, PathParamError } from '@tanstack/router-core' export { RouterProvider, RouterContextProvider } from './RouterProvider' export type { RouterProps } from './RouterProvider' diff --git a/packages/vue-router/src/index.tsx b/packages/vue-router/src/index.tsx index 2e14e349f0..a37664b7f0 100644 --- a/packages/vue-router/src/index.tsx +++ b/packages/vue-router/src/index.tsx @@ -271,7 +271,7 @@ export type { export { createRouter, Router } from './router' -export { lazyFn, SearchParamError } from '@tanstack/router-core' +export { lazyFn, SearchParamError, PathParamError } from '@tanstack/router-core' export { RouterProvider, RouterContextProvider } from './RouterProvider' export type { RouterProps } from './RouterProvider'