remix-run/react-router · error

Prerender: Failed to start Vite preview server

Error message

Prerender: Failed to start Vite preview server

What it means

The prerender plugin serves your built app through a temporary Vite preview server (port 0, silent logging) and issues requests against it. If `vite.preview()` throws — bad build output, config errors, port/host problems — the error is wrapped with this message and the original error attached as `cause`. The prerender cannot proceed without a server to render through.

Solutions

  1. Inspect `error.cause` — it carries the underlying Vite error
  2. Ensure the client (and server) build completed successfully before prerendering
  3. Remove or scope custom `preview` config in vite.config.ts that conflicts with the plugin's `port: 0, open: false` settings
  4. On CI, verify the environment allows binding a loopback port

Example fix

// before (vite.config.ts) — conflicting preview config
preview: { port: 4173, strictPort: true, host: '0.0.0.0' },

// after — let the prerender plugin pick an ephemeral port
// (remove the custom preview block or drop strictPort)
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm build output exists before prerendering
import fs from 'node:fs';
for (const p of ['build/client/index.html', 'build/server/index.js']) {
  if (!fs.existsSync(p)) throw new Error(`Missing ${p} — run the build first`);
}

Try / catch

try {
  await startPreviewServer(viteConfig);
} catch (e) {
  if (e instanceof Error && e.message.includes('Failed to start Vite preview server')) {
    // inspect e.cause — the underlying Vite error — before rethrowing
    console.error(e.cause);
  }
  throw e;
}

Prevention

When it happens

Trigger: `vite.preview()` rejecting: missing or malformed `build/client` output (build step skipped or failed silently); invalid `preview` config in vite.config.ts; `configFile` pointing at a config that errors when loaded in preview mode; port allocation failures on constrained CI.

Common situations: Running prerender without a preceding successful build; custom `vite.preview` settings (host, strictPort, https) that break headless startup; CI sandboxes blocking listen() on the requested host.

Related errors


AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18). Data as JSON: /api/errors/fcd9d19c4a981b33. Report an issue: GitHub.

Appendix: source

Thrown at packages/react-router-dev/vite/plugins/prerender.ts:561

  });
}

async function startPreviewServer(
  viteConfig: Vite.ResolvedConfig,
): Promise<Vite.PreviewServer> {
  const vite = await import("vite");

  try {
    return await vite.preview({
      configFile: viteConfig.configFile,
      logLevel: "silent",
      preview: {
        port: 0,
        open: false,
      },
    });
  } catch (error) {
    throw new Error("Prerender: Failed to start Vite preview server", {
      cause: error,
    });
  }
}

function getResolvedUrl(previewServer: Vite.PreviewServer): URL {
  const baseUrl = previewServer.resolvedUrls?.local[0];

  if (!baseUrl) {
    throw new Error(
      "Prerender: No resolved URL is available from the Vite preview server",
    );
  }

  return new URL(baseUrl);
}

View on GitHub (pinned to 6beaca3952)