{"record":{"id":"84a8a1ef13093717","repo":"charmbracelet/crush","slug":"server-error-v","errorCode":null,"errorMessage":"server error: %v","messagePattern":"server error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/server.go","lineNumber":79,"sourceCode":"\n\t\terrch := make(chan error, 1)\n\t\tsigch := make(chan os.Signal, 1)\n\t\tsigs := []os.Signal{os.Interrupt}\n\t\tsigs = append(sigs, addSignals(sigs)...)\n\t\tsignal.Notify(sigch, sigs...)\n\n\t\tgo func() {\n\t\t\terrch <- srv.ListenAndServe()\n\t\t}()\n\n\t\tselect {\n\t\tcase <-sigch:\n\t\t\tslog.Info(\"Received interrupt signal...\")\n\t\tcase err = <-errch:\n\t\t\tif err != nil && !errors.Is(err, server.ErrServerClosed) {\n\t\t\t\t_ = srv.Close()\n\t\t\t\tslog.Error(\"Server error\", \"error\", err)\n\t\t\t\treturn fmt.Errorf(\"server error: %v\", err)\n\t\t\t}\n\t\t}\n\n\t\tif errors.Is(err, server.ErrServerClosed) {\n\t\t\treturn nil\n\t\t}\n\n\t\tctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second)\n\t\tdefer cancel()\n\n\t\tslog.Info(\"Shutting down...\")\n\n\t\tif err := srv.Shutdown(ctx); err != nil {\n\t\t\tslog.Error(\"Failed to shutdown server\", \"error\", err)\n\t\t\treturn fmt.Errorf(\"failed to shutdown server: %v\", err)\n\t\t}\n\n\t\treturn nil","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/cmd/server.go#L61-L97","documentation":"The `server` command runs an HTTP server and watches an error channel. When any goroutine pushes a non-nil error that is not http.ErrServerClosed, the command closes the server and returns this wrapped error, causing the CLI to exit non-zero. It is the top-level propagation point for any runtime failure inside the server loop.","triggerScenarios":"Any error sent on errch (e.g. http.Server Serve/ListenAndServe failing: port already in use, permission denied on socket, TLS cert issues) that is not server.ErrServerClosed.","commonSituations":"Port 8080 (or configured port) already bound by another process; running without privileges on a low port; interface/address misconfiguration; TLS certificate files missing or invalid.","solutions":["Check that the configured listen address/port is free (lsof -i :PORT or ss -ltnp) and stop the conflicting process or change the port","Run the command with sufficient privileges or use a port >=1024 if binding a privileged port","Inspect the 'Server error' slog line printed just before this error for the root cause","If shutting down intentionally, ensure only ErrServerClosed is sent or filtered correctly"],"exampleFix":"// before\nreturn fmt.Errorf(\"server error: %v\", err)\n// after\nreturn fmt.Errorf(\"server error: %w\", err) // preserve wrap for errors.Is checks downstream","handlingStrategy":"try-catch","validationCode":"// before starting the server\nif l, err := net.Listen(\"tcp\", addr); err != nil {\n    return fmt.Errorf(\"address %s unavailable: %w\", addr, err)\n} else {\n    l.Close()\n}","typeGuard":null,"tryCatchPattern":"if err := runServer(ctx); err != nil {\n    if errors.Is(err, server.ErrServerClosed) {\n        return nil // intentional shutdown\n    }\n    slog.Error(\"Server failed\", \"error\", err)\n    os.Exit(1)\n}","preventionTips":["Probe the listen address with net.Listen before starting","Run services on unprivileged ports >=1024","Always compare with errors.Is(err, server.ErrServerClosed) instead of == so wrapped errors match","Use %w not %v when wrapping so callers can inspect causes"],"tags":["go","http-server","cli","network"],"backgroundTag":"address-already-in-use","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}