{"record":{"id":"528d19340badcc8d","repo":"vitessio/vitess","slug":"listener-closed-w","errorCode":null,"errorMessage":"listener closed: %w","messagePattern":"listener closed: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"go/vt/vtadmin/grpcserver/server.go","lineNumber":253,"sourceCode":"\t// Start the servers\n\tgo func() {\n\t\terr := s.gRPCServer.Serve(grpcLis)\n\t\terr = fmt.Errorf(\"grpc server stopped: %w\", err)\n\t\tlog.Warn(fmt.Sprint(err))\n\t\tshutdown <- err\n\t}()\n\n\tgo func() {\n\t\terr := http.Serve(anyLis, s.router)\n\t\terr = fmt.Errorf(\"http server stopped: %w\", err)\n\t\tlog.Warn(fmt.Sprint(err))\n\t\tshutdown <- err\n\t}()\n\n\t// Start muxing connections\n\tgo func() {\n\t\terr := lmux.Serve()\n\t\terr = fmt.Errorf(\"listener closed: %w\", err)\n\t\tlog.Warn(fmt.Sprint(err))\n\t\tshutdown <- err\n\t}()\n\n\tfor service := range s.gRPCServer.GetServiceInfo() {\n\t\ts.healthServer.SetServingStatus(service, healthpb.HealthCheckResponse_SERVING)\n\t}\n\n\ts.setServing(true)\n\tlog.Info(fmt.Sprintf(\"server %s listening on %s\", s.name, s.opts.Addr))\n\n\treason := <-shutdown\n\tlog.Warn(fmt.Sprintf(\"graceful shutdown triggered by: %v\", reason))\n\n\tif s.opts.LameDuckDuration > 0 {\n\t\tlog.Info(fmt.Sprintf(\"entering lame duck period for %v\", s.opts.LameDuckDuration))\n\t\ts.healthServer.Shutdown()\n\t\ttime.Sleep(s.opts.LameDuckDuration)","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtadmin/grpcserver/server.go#L235-L271","documentation":"vtadmin muxes gRPC and HTTP traffic over one port using a connection multiplexer (lmux); this error wraps the error returned when `lmux.Serve()` exits. Because the muxer feeds both the gRPC and HTTP servers, its failure or closure triggers a full shutdown via the shutdown channel. Typically this is observed when vtadmin is shutting down and the muxed listener is closed.","triggerScenarios":"`lmux.Serve()` returns: the underlying listener was closed (e.g., by Close()), an accept loop failed, or the muxer was already closed.","commonSituations":"Graceful vtadmin shutdown ordering (Close closes listener -> lmux.Serve returns); listener creation failed upstream; network accept errors on the serving port.","solutions":["Read the wrapped error to see whether the listener was closed deliberately or errored.","During shutdown, ignore as expected teardown noise.","Ensure ordering: start lmux.Serve before handing listeners to gRPC/HTTP and close via the single Close path.","If it occurs at startup, verify the configured port is free and bindable."],"exampleFix":"// before\nshutdown <- err\n// after\nif err != nil && !errors.Is(err, net.ErrClosed) {\n\tlog.Warn(fmt.Sprintf(\"listener closed: %v\", err))\n}\nshutdown <- err","handlingStrategy":"try-catch","validationCode":"if ln, err := net.Listen(\"tcp\", addr); err != nil {\n\tlog.Fatalf(\"cannot bind %s: %v\", addr, err)\n} else {\n\tln.Close()\n}","typeGuard":"func isListenerClosed(err error) bool {\n\treturn errors.Is(err, net.ErrClosed)\n}","tryCatchPattern":"err := <-shutdownCh\nif errors.Is(err, net.ErrClosed) {\n\tlog.Info(\"muxed listener closed during shutdown\")\n} else {\n\tlog.Error(fmt.Sprintf(\"lmux failed: %v\", err))\n}","preventionTips":["Start lmux.Serve before the gRPC/HTTP goroutines consume the muxed listener.","Close the listener only via the single Close() path.","Pre-verify the serving port is free and bindable in deployment checks."],"tags":["listener","mux","server-lifecycle","vtadmin"],"backgroundTag":"listener-closed","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}