remix-run/react-router · error · Error

Prerender: Failed to start Vite preview server

Error message

Prerender: Failed to start Vite preview server

What it means

Thrown by startPreviewServer() during the prerender data flow when Vite's preview() call rejects. The original error is preserved on the `cause` property so the underlying Vite failure (port binding, missing build output, invalid config) is inspectable. This is a wrapper that contextualizes a low-level Vite failure inside the React Router prerender pipeline.

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 1fd704a7da)

Solutions

  1. Inspect err.cause for the real Vite error (port, config, or missing artifacts) and address that first.
  2. Run `pnpm build` (or the project's production build) before invoking the prerender step so dist/ exists.
  3. Validate vite.config.ts loads cleanly with `vite preview` standalone to isolate plugin/config errors.
  4. Clear stale build output (`pnpm run clean` / remove dist and .vite) and rebuild.
  5. Ensure the configFile referenced by the resolved Vite config exists and has no syntax/import errors.

Example fix

// before: prerender invoked on a project whose dist/ was never built
// run a production build first
//   pnpm build
// then re-run the prerender pipeline

// if a custom vite.config breaks preview, isolate it:
//   npx vite preview --config vite.config.ts
// fix the reported error, then retry prerender
Defensive patterns

Strategy: try-catch

Validate before calling

// before invoking prerender, confirm a build exists
import { existsSync } from 'node:fs';
import { resolve } from 'node:path';
const distExists = existsSync(resolve(process.cwd(), 'build', 'client')) || existsSync(resolve(process.cwd(), 'dist'));
if (!distExists) throw new Error('Run `pnpm build` before prerendering.');

Try / catch

try {
  await runPrerenderPipeline();
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Prerender: Failed to start Vite preview server') && e.cause) {
    console.error('Underlying Vite error:', e.cause);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the prerender pipeline (build with prerender config in data mode) which internally invokes vite.preview({ configFile, logLevel:'silent', preview:{ port:0, open:false } }). Any rejection from vite.preview() — e.g. no built dist to preview, a broken vite.config, EADDRINUSE despite port:0 (rare), or an invalid configFile path — is caught and re-thrown as this error.

Common situations: Running a prerender build without first running a successful production build (dist/ missing or stale). A vite.config.ts that throws at preview time (custom preview plugins). Upgrading Vite to a version whose preview API changed. CI environments where the build step was skipped or cached output was pruned.

Related errors


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