Skip to content

Nested dynamic API route causes invalid Rollup chunkFileNames placeholder #4289

@old9

Description

@old9

Environment

Nitro: 3.0.260522-beta
Node.js: v22.22.3
npm: 10.9.8
Vite: 7.3.3
Rollup: 4.60.4
OS: Windows, Git Bash
Nitro preset: node-server

Nitro is used through the Vite plugin:

import { defineConfig } from 'vite'
import { nitro } from 'nitro/vite'

export default defineConfig({
  plugins: [
    nitro({
      serverDir: './server',
      preset: 'node-server',
    }),
  ],
})

Reproduction

Repository link: https://github.com/old9/nitro_issue_demo.

Minimal reproduction structure:

package.json
index.html
vite.config.ts
server/api/applications/[id]/context.get.ts

server/api/applications/[id]/context.get.ts:

export default defineEventHandler(event => {
  return {
    ok: true,
    id: getRouterParam(event, 'id'),
  }
})

Steps:

npm install
npm run build

Describe the bug

Building a Vite project with Nitro fails when the server API route is nested under a dynamic route parameter directory, for example:

server/api/applications/[id]/context.get.ts

The client build succeeds, but the Nitro server build fails with this Rollup error:

"[id]" is not a valid placeholder in the "output.chunkFileNames" pattern.

Expected behavior: Nitro should build successfully. Dynamic route params should not be emitted into Rollup filename patterns as raw [id] placeholders.

Observed behavior: Nitro appears to generate a route-derived chunk filename pattern containing [id], and Rollup interprets it as an unsupported chunkFileNames placeholder.

Rollup is behaving correctly here: valid chunkFileNames placeholders include [name], [hash], and [format], but not [id]. The invalid pattern seems to originate from Nitro's server chunk naming for nested dynamic routes.

Additional context

The issue can be worked around by overriding Nitro's server chunk file names so route params are not used in Rollup filename patterns:

nitro({
  serverDir: './server',
  preset: 'node-server',
  rollupConfig: {
    output: {
      chunkFileNames: '_chunks/[name]-[hash].mjs',
    },
  },
})

This is only a workaround. Nitro should probably escape or sanitize dynamic route param segments before using route-derived strings as Rollup output filename patterns.

Logs

> build
> vite build

ℹ Using index.html as renderer template.

[nitro] ◐ Building [Client]
vite v7.3.3 building client environment for production...
transforming...
✓ 1 modules transformed.
rendering chunks...
computing gzip size...
.output/public/index.html  0.06 kB │ gzip: 0.08 kB
✓ built in 105ms

[nitro] ◐ Building [Nitro] (preset: node-server, compatibility: 2026-05-28)
[nitro] ✔ Generated public .output/public
vite v7.3.3 building nitro environment for production...
transforming...
✓ 52 modules transformed.
✗ Build failed in 752ms
error during build:
"[id]" is not a valid placeholder in the "output.chunkFileNames" pattern.
    at getRollupError (file:///D:/sandbox/nitro_issue/node_modules/rollup/dist/es/shared/parseAst.js:406:41)
    at error (file:///D:/sandbox/nitro_issue/node_modules/rollup/dist/es/shared/parseAst.js:402:42)
    at file:///D:/sandbox/nitro_issue/node_modules/rollup/dist/es/shared/node-entry.js:18561:20
    at String.replace (<anonymous>)
    at renderNamePattern (file:///D:/sandbox/nitro_issue/node_modules/rollup/dist/es/shared/node-entry.js:18559:20)
    at Chunk.getPreliminaryFileName (file:///D:/sandbox/nitro_issue/node_modules/rollup/dist/es/shared/node-entry.js:18907:24)
    at Chunk.getFileName (file:///D:/sandbox/nitro_issue/node_modules/rollup/dist/es/shared/node-entry.js:18888:38)
    at Chunk.getImportPath (file:///D:/sandbox/nitro_issue/node_modules/rollup/dist/es/shared/node-entry.js:18891:54)
    at Chunk.setDynamicImportResolutions (file:///D:/sandbox/nitro_issue/node_modules/rollup/dist/es/shared/node-entry.js:19591:173)
    at Chunk.renderModules (file:///D:/sandbox/nitro_issue/node_modules/rollup/dist/es/shared/node-entry.js:19503:14)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions