{"record":{"id":"aa81da1d4e27e892","repo":"github/github-mcp-server","slug":"http-server-error-w","errorCode":null,"errorMessage":"HTTP server error: %w","messagePattern":"HTTP server error: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/http/server.go","lineNumber":229,"sourceCode":"\n\tgo func() {\n\t\t<-ctx.Done()\n\t\tshutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancel()\n\t\tlogger.Info(\"shutting down server\")\n\t\tif err := httpSvr.Shutdown(shutdownCtx); err != nil {\n\t\t\tlogger.Error(\"error during server shutdown\", \"error\", err)\n\t\t}\n\t}()\n\n\tif cfg.ExportTranslations {\n\t\t// Once server is initialized, all translations are loaded\n\t\tdumpTranslations()\n\t}\n\n\tlogger.Info(\"HTTP server listening\", \"addr\", addr)\n\tif err := httpSvr.ListenAndServe(); err != nil && err != http.ErrServerClosed {\n\t\treturn fmt.Errorf(\"HTTP server error: %w\", err)\n\t}\n\n\tlogger.Info(\"server stopped gracefully\")\n\treturn nil\n}\n\n// resolveListenAddress returns the address string passed to http.Server.\n// When host is empty the server binds to all interfaces on the given port;\n// otherwise host and port are joined into a single address.\nfunc resolveListenAddress(host string, port int) string {\n\tif host == \"\" {\n\t\treturn fmt.Sprintf(\":%d\", port)\n\t}\n\treturn net.JoinHostPort(host, strconv.Itoa(port))\n}\n\nfunc initGlobalToolScopeMap(t translations.TranslationHelperFunc, hostType utils.HostType) error {\n\t// Build inventory with all tools to extract scope information","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/pkg/http/server.go#L211-L247","documentation":"httpSvr.ListenAndServe returned an error other than http.ErrServerClosed - the listener could not serve. The overwhelmingly common cause is the listen address being unavailable: port already in use (EADDRINUSE), permission denied on a privileged port, or the address not being assignable on this host.","triggerScenarios":"Another process (or a second instance of this server) already bound the configured host:port; binding a port below 1024 as non-root; container port conflicts; stale process from a previous run.","commonSituations":"Running two copies of the server locally; port collisions in docker-compose/Kubernetes; switching to port 80/443 without CAP_NET_BIND_SERVICE.","solutions":["Find and stop the process holding the port (ss -ltnp / lsof -i :PORT) or change the port","Use an unprivileged port (>1024) or grant CAP_NET_BIND_SERVICE","Check the logged 'HTTP server listening' address matches the intended interface"],"exampleFix":"// before\nport := 80\n\n// after\nport := 8080 // unprivileged; map 80->8080 at the load balancer/container level","handlingStrategy":"try-catch","validationCode":"// pre-flight: verify the address is bindable before full startup\nln, err := net.Listen(\"tcp\", resolveListenAddress(cfg.Host, cfg.Port))\nif err != nil {\n\treturn fmt.Errorf(\"address in use: %w\", err)\n}\n_ = ln.Close()","typeGuard":null,"tryCatchPattern":"if err := httpSvr.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {\n\tif errors.Is(err, syscall.EADDRINUSE) {\n\t\t// port collision: fail fast with an actionable message\n\t\treturn fmt.Errorf(\"port %d already in use: %w\", cfg.Port, err)\n\t}\n\treturn fmt.Errorf(\"HTTP server error: %w\", err)\n}","preventionTips":["Check for listeners on the port before starting a second instance","Use unprivileged ports and map them at the edge","Rely on graceful shutdown signals so old instances release ports promptly"],"tags":["network","startup","configuration"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}