vitessio/vitess · warning

http server stopped: %w

Error message

http server stopped: %w

What it means

vtadmin runs an HTTP server on a multiplexed listener alongside gRPC; this error wraps whatever error `http.Serve` returns when that HTTP server stops. The wrapped error is logged as a warning and pushed to the shutdown channel to tear down vtadmin. Like the gRPC counterpart, it fires during normal graceful shutdown, so its presence alone is not a failure.

Source

Thrown at go/vt/vtadmin/grpcserver/server.go:245

		if !strings.HasPrefix(s.opts.MetricsEndpoint, "/") {
			s.opts.MetricsEndpoint = "/" + s.opts.MetricsEndpoint
		}

		grpc_prometheus.Register(s.gRPCServer)
		s.router.Handle(s.opts.MetricsEndpoint, promhttp.Handler())
	}

	// Start the servers
	go func() {
		err := s.gRPCServer.Serve(grpcLis)
		err = fmt.Errorf("grpc server stopped: %w", err)
		log.Warn(fmt.Sprint(err))
		shutdown <- err
	}()

	go func() {
		err := http.Serve(anyLis, s.router)
		err = fmt.Errorf("http server stopped: %w", err)
		log.Warn(fmt.Sprint(err))
		shutdown <- err
	}()

	// Start muxing connections
	go func() {
		err := lmux.Serve()
		err = fmt.Errorf("listener closed: %w", err)
		log.Warn(fmt.Sprint(err))
		shutdown <- err
	}()

	for service := range s.gRPCServer.GetServiceInfo() {
		s.healthServer.SetServingStatus(service, healthpb.HealthCheckResponse_SERVING)
	}

	s.setServing(true)
	log.Info(fmt.Sprintf("server %s listening on %s", s.name, s.opts.Addr))

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Inspect the wrapped error to distinguish expected shutdown from a real accept failure.
  2. If at shutdown, treat as expected and filter http.ErrServerClosed in logs.
  3. Ensure the muxed listener (lmux/anyLis) is closed exactly once, by vtadmin's Close path only.
  4. Check for duplicate Serve calls on the same listener.

Example fix

// before
log.Warn(fmt.Sprint(err))
// after
if err != nil && !errors.Is(err, http.ErrServerClosed) {
	log.Warn(fmt.Sprintf("http server stopped: %v", err))
}
Defensive patterns

Strategy: try-catch

Type guard

func isExpectedHTTPStop(err error) bool {
	return err == nil || errors.Is(err, http.ErrServerClosed) || errors.Is(err, net.ErrClosed)
}

Try / catch

err := <-shutdownCh
if !isExpectedHTTPStop(err) {
	log.Error(fmt.Sprintf("http server failed: %v", err))
}

Prevention

When it happens

Trigger: `http.Serve(anyLis, s.router)` returns: the muxed listener is closed, http.Server is already serving on that listener, or the listener reports an accept error.

Common situations: vtadmin process shutting down (lmux closed); another shutdown path closed the shared listener first; misconfiguration where the same listener was handed to two servers.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/232fba76514fcd88. Report an issue: GitHub.