Skip to content

vite.preview() called during static prerendering doesn't forward configLoader, causing preview server to crash (EASY FIX) #7593

@sebastienbarre

Description

@sebastienbarre

Which project does this relate to?

Start

Describe the bug

Hopefully this explanation is detailed enough, but the fix is only one line.

The bug

When using tanstackStart({ prerender: { enabled: true } }) with a monorepo where --configLoader native is required to launch Vite, the prerender server will crash.

Root cause

After the client and SSR builds complete, @tanstack/start-plugin-core/src/vite/prerender.ts spins up a Vite preview server to handle prerender requests (here):

// packages/start-plugin-core/src/vite/prerender.ts
async function startPreviewServer(viteConfig: ResolvedConfig): Promise<PreviewServer> {
  const vite = await import('vite')
  return await vite.preview({
    configFile: viteConfig.configFile,
    preview: { port: 0, open: false },
    // ← configLoader is NOT forwarded
  })
}

This calls vite.preview() without forwarding the configLoader from the original invocation. The configLoader field defaults to 'bundle' (I need native instead), which makes Vite re-parse vite.config.ts using Rolldown's internal config bundler. This causes the preview server to crash because it doesn't have the custom condition from NODE_OPTIONS="--conditions=@repo/source" that would allow it to resolve any package export that requires said custom condition.

Complete minimal reproducer

https://github.com/sebastienbarre-forks/tanstack-start-example-basic-static-crash

Steps to Reproduce the Bug

$ pnpm install
$ pnpm build

You should see:

✓ built in 263ms
failed to load config from/start-basic-static/packages/app/vite.config.ts
error during build:
Error: Failed to start the Vite preview server for prerendering
    at startPreviewServer (file:///start-basic-static/node_modules/.pnpm/@tanstack+start-plugin-core@1.171.17_@tanstack+react-router@1.170.15_react-dom@19.2.7_r_271e9cf7e315f271d5a87ddafbf33008/node_modules/@tanstack/start-plugin-core/dist/esm/vite/prerender.js:41:9)

If you follow the above link to the bundled file, you will find the throw:

async function startPreviewServer(viteConfig) {
	const vite = await import("vite");
	try {
		return await vite.preview({
			configFile: viteConfig.configFile,
			preview: {
				port: 0,
				open: false
			}
		});
	} catch (error) {
		throw new Error("Failed to start the Vite preview server for prerendering", { cause: error });
	}
}

The call to vite.preview() is just missing one line to propagate the config loader (see below).

Expected behavior

The vite.preview() call in startPreviewServer should forward configLoader (and ideally all relevant InlineConfig fields) from the resolved config of the original build. This is easy, a one line change (I tested it by hard-coding it in the shipped dist/esm/vite/prerender.js):

return await vite.preview({
  configFile: viteConfig.configFile,
  configLoader: viteConfig.inlineConfig?.configLoader,  // ← propagate
  preview: { port: 0, open: false },
})

Note: configLoader is part of Vite 8's InlineConfig type

Screenshots or Videos

N/A

Platform

  • TanStack Start 1.168.25 (I believe)
  • macOS
  • Browser: N/A
  • Bundler: Vite
  • Bundler Version: 8.0.16

Additional context

Thank you!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    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