Repository: vercel/next.js
Pull request: Reuse resolvedPathname from prepare

This applies the fixed `resolvedPathname` from https://github.com/vercel/next.js/pull/81144 to the other route handlers that were trying to build this value as well so we use single source of truth. 

Validated against our deploy tests https://github.com/vercel/vercel/actions/runs/16038359282/job/45254959257?pr=13520 and https://github.com/vercel/next.js/actions/runs/16038663582/job/45255920639

Changed files:
filename: packages/next/src/build/templates/app-route.ts
patch: @@ -21,7 +21,6 @@ import {
   fromNodeOutgoingHttpHeaders,
   toNodeOutgoingHttpHeaders,
 } from '../../server/web/utils'
-import { decodePathParams } from '../../server/lib/router-utils/decode-path-params'
 import { getCacheControlHeader } from '../../server/lib/cache-control'
 import { INFINITE_CACHE, NEXT_CACHE_TAGS_HEADER } from '../../lib/constants'
 import { NoFallbackError } from '../../shared/lib/no-fallback-error.external'
@@ -114,30 +113,16 @@ export async function handler(
     buildId,
     params,
     nextConfig,
-    parsedUrl,
     isDraftMode,
     prerenderManifest,
     routerServerContext,
     isOnDemandRevalidate,
     revalidateOnlyGenerated,
+    resolvedPathname,
   } = prepareResult
 
   const normalizedSrcPage = normalizeAppPath(srcPage)
 
-  // TODO: rework this to not be necessary as a middleware
-  // rewrite should not need to pass this context like this
-  // maybe we rely on rewrite header instead
-  let resolvedPathname = getRequestMeta(req, 'rewroteURL')
-
-  if (!resolvedPathname) {
-    resolvedPathname = parsedUrl.pathname || '/'
-  }
-
-  if (resolvedPathname === '/index') {
-    resolvedPathname = '/'
-  }
-  resolvedPathname = decodePathParams(resolvedPathname)
-
   let isIsr = Boolean(
     prerenderManifest.dynamicRoutes[normalizedSrcPage] ||
       prerenderManifest.routes[resolvedPathname]

filename: packages/next/src/build/templates/pages.ts
patch: @@ -34,15 +34,12 @@ import { getRedirectStatus } from '../../lib/redirect-status'
 import { CACHE_ONE_YEAR } from '../../lib/constants'
 import { sendRenderResult } from '../../server/send-payload'
 import RenderResult from '../../server/render-result'
-import { decodePathParams } from '../../server/lib/router-utils/decode-path-params'
 import { toResponseCacheEntry } from '../../server/response-cache/utils'
 import { NoFallbackError } from '../../shared/lib/no-fallback-error.external'
 import { RedirectStatusCode } from '../../client/components/redirect-status-code'
 import { isBot } from '../../shared/lib/router/utils/is-bot'
 import { addPathPrefix } from '../../shared/lib/router/utils/add-path-prefix'
 import { removeTrailingSlash } from '../../shared/lib/router/utils/remove-trailing-slash'
-import { pathHasPrefix } from '../../shared/lib/router/utils/path-has-prefix'
-import { normalizeLocalePath } from '../../shared/lib/i18n/normalize-locale-path'
 
 // Re-export the component (should be the default export).
 export default hoist(userland, 'default')
@@ -149,6 +146,7 @@ export async function handler(
     defaultLocale,
     routerServerContext,
     nextConfig,
+    resolvedPathname,
   } = prepareResult
 
   const isExperimentalCompile =
@@ -169,31 +167,6 @@ export async function handler(
   const is404Page = srcPage === '/404'
   const is500Page = srcPage === '/500'
   const isErrorPage = srcPage === '/_error'
-  const pathname = parsedUrl.pathname || '/'
-
-  // TODO: rework this to not be necessary as a middleware
-  // rewrite should not need to pass this context like this
-  // maybe we rely on rewrite header instead
-  let resolvedPathname = getRequestMeta(req, 'rewroteURL')
-
-  if (resolvedPathname) {
-    if (pathHasPrefix(resolvedPathname, '/_next/data/')) {
-      resolvedPathname = normalizeDataPath(resolvedPathname)
-    }
-
-    if (locale) {
-      resolvedPathname = normalizeLocalePath(
-        resolvedPathname,
-        nextConfig.i18n?.locales || []
-      ).pathname
-    }
-  } else {
-    resolvedPathname = pathname
-  }
-
-  if (resolvedPathname === '/index') {
-    resolvedPathname = '/'
-  }
 
   if (!routeModule.isDev && !isDraftMode && hasStaticProps) {
     cacheKey = `${locale ? `/${locale}` : ''}${
@@ -206,19 +179,13 @@ export async function handler(
       cacheKey = `${locale ? `/${locale}` : ''}${srcPage}${isAmp ? '.amp' : ''}`
     }
 
-    cacheKey = decodePathParams(cacheKey)
-
     // ensure /index and / is normalized to one key
     cacheKey = cacheKey === '/index' ? '/' : cacheKey
   }
 
   if (hasStaticPaths && !isDraftMode) {
     const decodedPathname = removeTrailingSlash(
-      decodePathParams(
-        locale
-          ? addPathPrefix(resolvedPathname, `/${locale}`)
-          : resolvedPathname
-      )
+      locale ? addPathPrefix(resolvedPathname, `/${locale}`) : resolvedPathname
     )
     const isPrerendered =
       Boolean(prerenderManifest.routes[decodedPathname]) ||

filename: packages/next/src/server/route-modules/route-module.ts
patch: @@ -52,13 +52,13 @@ import type { RouteKind } from '../route-kind'
 import type { BaseNextRequest } from '../base-http'
 import type { I18NConfig, NextConfigComplete } from '../config-shared'
 import ResponseCache, { type ResponseGenerator } from '../response-cache'
+import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths'
 import {
   RouterServerContextSymbol,
   routerServerGlobal,
   type RouterServerContext,
 } from '../lib/router-utils/router-server-context'
 import { decodePathParams } from '../lib/router-utils/decode-path-params'
-import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths'
 
 /**
  * RouteModuleOptions is the options that are passed to the route module, other
@@ -769,7 +769,9 @@ export abstract class RouteModule<
     if (resolvedPathname === '/index') {
       resolvedPathname = '/'
     }
-    resolvedPathname = decodePathParams(resolvedPathname)
+    try {
+      resolvedPathname = decodePathParams(resolvedPathname)
+    } catch (_) {}
 
     return {
       query,