vitejs/vite · error · Error

rolldownOptions.input should not be an html file when buildi

Error message

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

What it means

In resolveRolldownOptions (build.ts:627), when building for SSR (environment consumer === 'server') and the resolved input is a string ending in .html, Vite throws. An HTML entry is a client-side document and cannot serve as an SSR bundle entry; SSR needs a dedicated JS/TS entry module.

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 89620f09af)

Solutions

  1. Provide a dedicated SSR entry, e.g. build: { ssr: 'src/entry-server.ts' }.
  2. Or set rollupOptions/rolldownOptions.input to a .js/.ts server entry instead of index.html.
  3. Confirm the environment's consumer is actually 'server' only when you intend SSR.

Example fix

// before
build: { ssr: true } // input defaults to index.html
// after
build: { ssr: 'src/entry-server.ts' }
Defensive patterns

Strategy: validation

Validate before calling

const ssrInput = config.build?.ssr
if (ssrInput === true || typeof ssrInput === 'string') {
  const entry = typeof ssrInput === 'string' ? ssrInput : config.build?.rolldownOptions?.input
  if (typeof entry === 'string' && entry.endsWith('.html')) {
    throw new Error('SSR build entry must not be an .html file')
  }
}

Type guard

function isNonHtmlSsrEntry(entry: unknown): boolean {
  return typeof entry === 'string' && !entry.endsWith('.html')
}

Prevention

When it happens

Trigger: Running an SSR build where the input defaults to index.html (because no SSR entry was given), or explicitly passing an .html file as rolldownOptions.input while consumer is 'server'.

Common situations: Forgetting to set build.ssr to a server entry string and letting it default to index.html; migrating from client-only to SSR without adding a server entry.

Related errors


AI-assisted analysis of vitejs/vite@89620f09af (2026-08-03). Data as JSON: /data/errors/768909f032203b37.json. Report an issue: GitHub.