cypress-io/cypress · error · Error

Missing vite dev server port.

Error message

Missing vite dev server port.

What it means

Thrown by @cypress/vite-dev-server after `server.listen()` resolves, when `server.config.server.port` is falsy. Vite normally assigns a port (random if 0 was requested); a missing port means the server did not bind or did not report its bound port back into config. Cypress needs a concrete port to point the browser at the dev server, so it refuses to proceed.

Solutions

  1. In your viteConfig passed to devServer, set an explicit `server.port` (e.g. 5173) instead of 0.
  2. Ensure no Vite plugin in your config overwrites server.port after listen.
  3. Update Vite to a version known to populate config.server.port after listen (Vite 5/6/7 supported per the package's matrix).
  4. If you need an ephemeral port, read the actual bound port from `server.httpServer.address()` in a custom plugin rather than relying on config.server.port.

Example fix

// before
devServer: { bundler: 'vite', viteConfig: { server: { port: 0 } } }
// after
devServer: { bundler: 'vite', viteConfig: { server: { port: 5173, strictPort: true } } }
Defensive patterns

Strategy: validation

Validate before calling

// choose a concrete port before starting
const PORT = 5173
devServer: { bundler: 'vite', viteConfig: { server: { port: PORT, strictPort: true } } }

Type guard

const hasConcretePort = (cfg: any): boolean => typeof cfg?.server?.port === 'number' && cfg.server.port > 0

Prevention

When it happens

Trigger: Calling devServer.create then server.listen() where the resulting server.config.server.port is undefined/0/null. Occurs when the user's viteConfig sets `server.port: 0` without `strictPort`, when a plugin clears the port, when listen fails silently, or when a Vite version reports the bound port elsewhere than config.server.port.

Common situations: A user vite.config that sets `server: { port: 0 }` (request OS-assigned port) but a Vite version that does not backfill the chosen port into config; strictPort conflicts; a misbehaving plugin that mutates server config; running on a restricted environment where listen did not actually bind.

Related errors


AI-assisted analysis of cypress-io/cypress@0d85fdc912 (2026-08-12). Data as JSON: /api/errors/a8f47eee81e09290. Report an issue: GitHub.

Appendix: source

Thrown at npm/vite-dev-server/src/devServer.ts:45

  let majorVersion: number | undefined = undefined

  if (vite.version) {
    majorVersion = semverMajor(vite.version)
    debug(`Found vite version v${majorVersion}`)
  } else {
    debug(`vite version not found`)
  }

  debug('Creating Vite Server')
  const server = await devServer.create(config, vite)

  debug('Vite server created')

  await server.listen()
  const { port } = server.config.server

  if (!port) {
    throw new Error('Missing vite dev server port.')
  }

  debug('Successfully launched the vite server on port', port)

  // Warm up the support file (always) and every spec (run mode only),
  // then waitForRequestsIdle, so Vite's deps optimizer has fully processed
  // any node_modules imports they pull in before the browser fetches
  // them. Skipping this can race a mid-test optimizer re-bundle and
  // surface "Failed to fetch dynamically imported module".
  //
  // Per-spec warmup is required: preprocessor or auto-import plugins can
  // inject node_modules imports during transform that Vite's static deps
  // scanner doesn't see, so the optimizer would otherwise first discover
  // them when the browser fetches the spec.
  //
  // In open mode (`isTextTerminal === false`), we skip the per-spec
  // warmup. The user picks specs interactively and is unlikely to run
  // every spec in the suite, so warming all of them up front would pay

View on GitHub (pinned to 0d85fdc912)