vercel / vercel/next.js

Dynamic `sitemap.ts` under a dynamic segment with `generateStaticParams` is collapsed to `/-/sitemap.xml` — per-param sitemaps no longer emitted (regression 16.2.3 → 16.3)

Open
#98,126 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dynamic Routes Metadata Output
Dominant language
JavaScript
Stars
142k
Forks
32.4k
Avg merge
2d 14h
Merged PRs (30d)
351

Description

Link to the code that reproduces this issue

https://github.com/Debbl/next-dynamic-sitemap-repro

To Reproduce
  1. Clone the reproduction repository and check out the plain repro commit — note that main HEAD additionally contains a pnpm patch (patches/next@16.3.4.patch) demonstrating the proposed fix, so building main as-is does NOT reproduce the bug:
    git clone https://github.com/Debbl/next-dynamic-sitemap-repro
    cd next-dynamic-sitemap-repro
    git checkout d2e42032449c7f92113ba0e591b2979f4fd1de55  # plain repro, no patch
    
  2. pnpm install
  3. pnpm build (runs next build with output: 'export')
  4. find out -name 'sitemap*'

The app has a dynamic [lang] segment with a code-based sitemap:

app/
  layout.js
  [lang]/
    page.js       # generateStaticParams -> en, zh
    sitemap.js    # generateStaticParams -> en, zh; export const dynamic = 'force-static'
Current vs. Expected behavior

Current behavior (next 16.3.4)

The sitemap route is collapsed to a single --placeholder pathname. generateStaticParams of sitemap.js is never invoked:

└   /-/sitemap.xml
  └ ● /-/sitemap.xml

Only out/-/sitemap.xml is emitted. out/en/sitemap.xml and out/zh/sitemap.xml do not exist, so any robots.txt pointing at /{lang}/sitemap.xml (the URLs previous versions produced, which search consoles have indexed) now 404s.

The same happens without output: 'export': prerender-manifest.json routes contains only /-/sitemap.xml, no /en/sitemap.xml / /zh/sitemap.xml entries.

Expected behavior (next 16.2.3)

└ ● /[lang]/sitemap.xml
  ├ /en/sitemap.xml
  └ /zh/sitemap.xml

out/en/sitemap.xml and out/zh/sitemap.xml are emitted (verify with pnpm add next@16.2.3 && rm -rf .next out && pnpm build).

Root cause analysis

This looks like fallout from #93873 ("Prerender static metadata under dynamic segments to canonical pathname"). That optimization is meant for static metadata files (e.g. /[id]/apple-icon.png), whose bytes don't depend on params. But the check matches by pathname shape only:

  • In isPageStatic (packages/next/src/build/utils.ts), isStaticMetadataFile(pathname) matches /[lang]/sitemap.xml and takes the buildStaticMetadataStaticPaths shortcut, skipping buildAppStaticPaths entirely — so the userland generateStaticParams exported by sitemap.js (which the metadata route loader re-exports via createReExportsCode) is never called.
  • One-liner showing the collapse:
    node -e "const{getStaticMetadataPrerenderPathname:f}=require('next/dist/lib/metadata/get-metadata-route.js');console.log(f('/[lang]/sitemap.xml'))"
    # → /-/sitemap.xml
    

A guard on the shortcut fixes it while keeping the #93873 optimization for true static files (static metadata files never export generateStaticParamsgetStaticAssetRouteCode emits none):

if (
  routeModule.definition.kind === RouteKind.APP_ROUTE &&
  isStaticMetadataFile(pathname) &&
  !segments.some((segment) => typeof segment.generateStaticParams === 'function')
) {

Verified locally against the repro: per-param sitemaps are restored, and a true static file (app/[lang]/icon.png) still collapses to a single out/-/icon.png.

Provide environment information
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin 25.5.0
Binaries:
  Node: 24.13.0
  npm: 11.6.2
  Yarn: N/A
  pnpm: 10.26.0
Relevant Packages:
  next: 16.3.4
  eslint-config-next: N/A
  react: 19.2.8
  react-dom: 19.2.8
  typescript: N/A
Next.js Config:
  output: export
Which area(s) are affected? (Select all that apply)

Metadata, Dynamic Routes, Output

Which stage(s) are affected? (Select all that apply)

next build (local)

Additional context
  • Regression window: 16.2.3 works, 16.3.4 does not (introduced by #93873, first shipped in the 16.3 line).
  • Reproduces locally with both next build + output: 'export' and a plain server build (the export just makes the missing files obvious; in a server build the collapsed route lands in prerenderManifest.routes as /-/sitemap.xml the same way).
  • Real-world impact: localized sitemaps under app/[lang]/sitemap.ts disappear from the build output, breaking robots.txt Sitemap: entries and search-console registrations that point at /{lang}/sitemap.xml.
  • Repro repo layout: commit d2e4203 is the plain reproduction; main HEAD (831e964) additionally applies the proposed fix as a pnpm patch (patches/next@16.3.4.patch) — building main shows the expected per-param output (out/en/sitemap.xml, out/zh/sitemap.xml) while a true static file (app/[lang]/icon.png) still collapses to a single out/-/icon.png, i.e. the #93873 optimization is preserved.
  • The repro README contains the 16.2.3 comparison steps and the root-cause notes; happy to open a PR with the guard described above.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with packages/next/src/build/utils.ts, the isPageStatic path, and getStaticMetadataPrerenderPathname; use the plain repro commit d2e4203 and run pnpm install, pnpm build, and find out -name 'sitemap*'. Done means per-param sitemap files appear for en and zh, while the static icon case remains collapsed; compare with the existing 16.2.3 output.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, next.js
Domain
build-system, web-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.