vitejs/vite · error · Error

Cannot call server.listen in middleware mode.

Error message

Cannot call server.listen in middleware mode.

What it means

Vite's dev server exposes a server.listen() method that binds the underlying HTTP server to a port. When createServer is called with server.middlewareMode set to true, Vite intentionally does not create an httpServer (it is null), so there is nothing to listen on. Calling server.listen() in that state throws because the connect-based middleware stack is meant to be mounted inside another HTTP server, not bound directly.

Source

Thrown at packages/vite/src/node/server/index.ts:1106

        return
      }
      return listen(port, ...args)
    }) as any
  } else {
    await initServer(false)
  }

  return server
}

async function startServer(
  server: ViteDevServer,
  hostname: Hostname,
  inlinePort?: number,
): Promise<void> {
  const httpServer = server.httpServer
  if (!httpServer) {
    throw new Error('Cannot call server.listen in middleware mode.')
  }

  const options = server.config.server
  const configPort = inlinePort ?? options.port
  // When using non strict port for the dev server, the running port can be different from the config one.
  // When restarting, the original port may be available but to avoid a switch of URL for the running
  // browser tabs, we enforce the previously used port, expect if the config port changed.
  const port =
    (!configPort || configPort === server._configServerPort
      ? server._currentServerPort
      : configPort) ?? DEFAULT_DEV_PORT
  server._configServerPort = configPort

  const serverPort = await httpServerStart(httpServer, {
    port,
    strictPort: options.strictPort,
    host: hostname.host,
    logger: server.config.logger,

View on GitHub (pinned to 89620f09af)

Solutions

  1. Remove the server.listen() call — in middleware mode you mount server.middlewares into your own HTTP app instead.
  2. Guard the call: only invoke server.listen() when server.config.server.middlewareMode is falsy.
  3. If you need a standalone listening server, remove middlewareMode: true from your config so Vite creates its own httpServer.

Example fix

// before
const server = await createServer({ server: { middlewareMode: true } })
await server.listen(3000)

// after (middleware mode: mount into your own app)
const server = await createServer({ server: { middlewareMode: true } })
app.use(server.middlewares)
app.listen(3000)
Defensive patterns

Strategy: validation

Validate before calling

// Before calling listen, check the server mode
const isMiddleware = server.config.server.middlewareMode
if (!isMiddleware) {
  await server.listen(port)
} else {
  // mount server.middlewares into your own HTTP app instead
  yourApp.use(server.middlewares)
}

Type guard

// Narrow a ViteDevServer to a listenable one
import type { ViteDevServer } from 'vite'

function isStandaloneServer(server: ViteDevServer): boolean {
  return !server.config.server.middlewareMode && server.httpServer != null
}

if (isStandaloneServer(server)) {
  await server.listen()
}

Try / catch

try {
  await server.listen(port)
} catch (e) {
  if (e instanceof Error && /middleware mode/i.test(e.message)) {
    // mount into your own app instead
    app.use(server.middlewares)
  } else {
    throw e
  }
}

Prevention

When it happens

Trigger: Calling server.listen() (or server.listen(port)) on a ViteDevServer instance created with config.server.middlewareMode === true. Also triggered by code paths that assume a standalone server exists, such as calling printUrls or restart on a middleware-mode server that internally invokes listen().

Common situations: Embedding Vite as middleware inside an existing Express/Koa/Fastify/Connect app via createServer({ server: { middlewareMode: true } }) and then forgetting the server is not standalone. Copy-pasting startup code from a non-middleware setup into a middleware-mode project. Plugins or framework integrations (e.g. Nuxt, SvelteKit) that call listen() unconditionally without checking middlewareMode.

Related errors


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