Which project does this relate to?
Router
Describe the bug
A path param value starting with $ is destroyed on full page load: the router rewrites the URL to /files/undefined via history.replaceState and the param value becomes the string "undefined".
With pathParamsAllowedCharacters: ['$'], the router itself generates such URLs: a <Link to="/files/$filePath" params={{ filePath: '$EXAMPLE_CODE/file.abap' }}> navigates to /files/$EXAMPLE_CODE%2Ffile.abap and works fine client-side, but reloading that URL mangles it.
Claude's root cause analysis: on mount, <Transitioner> round-trips the current location through buildLocation({ to: router.latestLocation.pathname, ... }). buildLocation treats to as a route template, so interpolatePath interprets the concrete segment $EXAMPLE_CODE%2Ffile.abap as a param placeholder named EXAMPLE_CODE%2Ffile.abap.
No such param exists, so it interpolates "undefined" (encodeParam(key, params, decoder) ?? "undefined" in router-core's path.ts). The rebuilt publicHref differs from the current one, so the broken location is committed with replace: true. A second symptom of the same ambiguity: the rebuilt to containing $ doesn't exactly match any route template, so buildLocation sets destRoutes = [] and skips all search middlewares for that navigation.
Complete minimal reproducer
https://stackblitz.com/edit/github-arh22bkl?file=src%2Fmain.tsx
Steps to Reproduce the Bug
- npm install && npm run dev
- Open http://localhost:5173/ and click the link — the URL becomes /files/$EXAMPLE_CODE%2Ffile.abap and the view renders the param correctly
- Reload the page
- The URL is replaced with /files/undefined and useParams() returns filePath: "undefined"
Expected behavior
As a user, I expected a page reload of a router-generated URL to render the same route with the same param value, but the URL is rewritten to /files/undefined and the param value is lost.
Screenshots or Videos
No response
Platform
- Router / Start Version: 1.169.2
- OS: macOS 26.5.1
- Browser: Chrome
- Browser Version: 149
- Bundler: vite
- Bundler Version: 8.0.10 (reproduction uses vite 7 as I ran into some napi crashes in Stackblitz)
Additional context
Workaround: percent-encode $ as %24 in the pathname (e.g. via history.replaceState) before the router parses the location.
Which project does this relate to?
Router
Describe the bug
A path param value starting with
$is destroyed on full page load: the router rewrites the URL to/files/undefinedviahistory.replaceStateand the param value becomes the string"undefined".With
pathParamsAllowedCharacters: ['$'], the router itself generates such URLs: a<Link to="/files/$filePath" params={{ filePath: '$EXAMPLE_CODE/file.abap' }}>navigates to/files/$EXAMPLE_CODE%2Ffile.abapand works fine client-side, but reloading that URL mangles it.Claude's root cause analysis: on mount,
<Transitioner>round-trips the current location throughbuildLocation({ to: router.latestLocation.pathname, ... }). buildLocationtreats to as a route template, sointerpolatePathinterprets the concrete segment$EXAMPLE_CODE%2Ffile.abapas a param placeholder namedEXAMPLE_CODE%2Ffile.abap.No such param exists, so it interpolates
"undefined"(encodeParam(key, params, decoder) ?? "undefined"in router-core'spath.ts). The rebuiltpublicHrefdiffers from the current one, so the broken location is committed withreplace: true. A second symptom of the same ambiguity: the rebuilt to containing$doesn't exactly match any route template, sobuildLocationsetsdestRoutes = []and skips all search middlewares for that navigation.Complete minimal reproducer
https://stackblitz.com/edit/github-arh22bkl?file=src%2Fmain.tsx
Steps to Reproduce the Bug
Expected behavior
As a user, I expected a page reload of a router-generated URL to render the same route with the same param value, but the URL is rewritten to /files/undefined and the param value is lost.
Screenshots or Videos
No response
Platform
Additional context
Workaround: percent-encode
$as%24in the pathname (e.g. viahistory.replaceState) before the router parses the location.