AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

Router.Replace/Push Restores Stale Hash From Initial URL On Dynamic Param Routes

Direct entry to a dynamic param route with a URL hash seeds the segment cache with a hashful canonical URL. Subsequent hashless router.replace/push calls then rebuild the target URL by appending an empty hash to the poisoned canonical URL, causing the browser to incorrectly retain the original hash. This breaks closing modals or any hash-keyed UI on deep links.

highConfidence 95%Next.jsAffected V16.3.0Affected V16.3.1-Canary.3

Origin Analysis

The initial-load call site in create-initial-router-state.ts derives canonicalUrl via createHrefFromUrl(location), which includes location.hash, and passes it to discoverKnownRoute without stripping the hash. In contrast, the navigation call site in navigation.ts explicitly strips the hash before storing. The poisoned cache entry causes segment-cache/navigation.ts to compute canonicalUrl + url.hash (empty string) as the original hashful URL, which is then used in history.replaceState.
1. Clone repro repo and run: npm install && npm run build && npm run start 2. Open http://localhost:3000/p/123#modal in a new tab 3. Click the button that calls router.replace('/p/123', { scroll: false }) 4. Observe URL remains /p/123#modal instead of /p/123

Fixing Code Block

Edge Case Audit

This one-line change makes initial canonical URLs hashless, consistent with the navigation path. Rollback by reverting the line if initial render logic unexpectedly depends on location.hash in canonicalUrl. Ensure test coverage for direct deep links with hashes on dynamic routes and for subsequent same-route hash navigation (hash should still be appended by the navigation site). No concurrency or threading concerns as this is client-side single-threaded code.

Ecosystem Topology