vitejs/vite · error · Error

rolldownOptions.input should not be an html file when…

Error message

rolldownOptions.input should not be an html file when building for SSR. Please specify a dedicated SSR entry.

What it means

When building for SSR (`environment.config.consumer === 'server'`), `resolveRolldownOptions` checks the resolved input: if it is a single string ending in `.html`, the build throws. HTML entries are for the browser; an SSR build needs a JavaScript/TS entry module, so the config is rejected with a directive to specify a dedicated SSR entry.

Solutions

  1. Set `build.ssr` to a server entry file: `build: { ssr: 'src/entry-server.ts' }`.
  2. Or set `build.rollupOptions.input` (the resolved input) to a `.js`/`.ts` server entry instead of `index.html`.
  3. Make sure `rolldownOptions.input` is not a `.html` path when targeting the SSR/server consumer.

Example fix

// before
build: { ssr: true } // falls back to index.html -> throws
// after
build: { ssr: 'src/entry-server.ts' }
Defensive patterns

Strategy: validation

Validate before calling

function assertSsrEntryNotHtml(cfg: { build?: { ssr?: unknown }; rollupOptions?: { input?: unknown } }) {
  const ssr = cfg.build?.ssr
  const input = cfg.rollupOptions?.input
  const resolvedInput = typeof ssr === 'string' ? ssr : input
  if (resolvedInput != null && typeof resolvedInput === 'string' && resolvedInput.endsWith('.html')) {
    throw new Error('SSR build input must not be an HTML file - set build.ssr to a .ts/.js entry')
  }
}

Type guard

function ssrEntryIsNotHtml(cfg: any): boolean {
  const i = typeof cfg?.build?.ssr === 'string' ? cfg.build.ssr : cfg?.rollupOptions?.input
  return !(typeof i === 'string' && i.endsWith('.html'))
}

Prevention

When it happens

Trigger: Setting `build.ssr: true` (or building an environment whose consumer is `server`) while the resolved input is an HTML file - typically the default `index.html` left because no SSR entry was configured. Also triggered by explicitly passing `rolldownOptions.input: 'index.html'` for an SSR build.

Common situations: Switching an app to SSR but forgetting to set `build.ssr` to a server entry path. Setting `ssr: true` (boolean) without an entry and relying on the default HTML input. Mixing SPA HTML entry with SSR build target.

Related errors


AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11). Data as JSON: /api/errors/768909f032203b37. Report an issue: GitHub.

Appendix: source

Thrown at packages/vite/src/node/build.ts:628

  const input = libOptions
    ? options.rolldownOptions.input ||
      (typeof libOptions.entry === 'string'
        ? resolve(libOptions.entry)
        : Array.isArray(libOptions.entry)
          ? libOptions.entry.map(resolve)
          : Object.fromEntries(
              Object.entries(libOptions.entry!).map(([alias, file]) => [
                alias,
                resolve(file),
              ]),
            ))
    : typeof options.ssr === 'string'
      ? resolve(options.ssr)
      : options.rolldownOptions.input ||
        (topLevelInput ?? resolve('index.html'))

  if (ssr && typeof input === 'string' && input.endsWith('.html')) {
    throw new Error(
      `rolldownOptions.input should not be an html file when building for SSR. ` +
        `Please specify a dedicated SSR entry.`,
    )
  }
  if (options.cssCodeSplit === false) {
    const inputs =
      typeof input === 'string'
        ? [input]
        : Array.isArray(input)
          ? input
          : Object.values(input)
    if (inputs.some((input) => input.endsWith('.css'))) {
      throw new Error(
        `When "build.cssCodeSplit: false" is set, "rolldownOptions.input" should not include CSS files.`,
      )
    }
  }

View on GitHub (pinned to b4d66fee14)