{"record":{"id":"419b9c40e6b27e51","repo":"nsqio/nsq","slug":"http-serve-error-s","errorCode":null,"errorMessage":"http.Serve() error - %s","messagePattern":"http\\.Serve\\(\\) error - (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/http_api/http_server.go","lineNumber":32,"sourceCode":"\tlogf lg.AppLogFunc\n}\n\nfunc (l logWriter) Write(p []byte) (int, error) {\n\tl.logf(lg.WARN, \"%s\", string(p))\n\treturn len(p), nil\n}\n\nfunc Serve(listener net.Listener, handler http.Handler, proto string, logf lg.AppLogFunc) error {\n\tlogf(lg.INFO, \"%s: listening on %s\", proto, listener.Addr())\n\n\tserver := &http.Server{\n\t\tHandler:  handler,\n\t\tErrorLog: log.New(logWriter{logf}, \"\", 0),\n\t}\n\terr := server.Serve(listener)\n\t// theres no direct way to detect this error because it is not exposed\n\tif err != nil && !errors.Is(err, net.ErrClosed) {\n\t\treturn fmt.Errorf(\"http.Serve() error - %s\", err)\n\t}\n\n\tlogf(lg.INFO, \"%s: closing %s\", proto, listener.Addr())\n\n\treturn nil\n}\n","sourceCodeStart":14,"sourceCodeEnd":39,"githubUrl":"https://github.com/nsqio/nsq/blob/85cf10c09c6c3c86160d6f0eb156f62d0efc1648/internal/http_api/http_server.go#L14-L39","documentation":"internal/http_api.Serve runs the HTTP accept loop for nsqd's, nsqlookupd's and nsqadmin's web listeners (http_server.go). It calls http.Server.Serve on the bound listener and treats net.ErrClosed as the normal shutdown signal; any other error from Serve is wrapped as 'http.Serve() error - %s' and returned, which propagates to main and exits the process. In practice Serve only errors on listener-level failures, since per-connection handler errors stay inside the server.","triggerScenarios":"The listener's fd breaks or cannot accept anymore without being explicitly Closed: file-descriptor exhaustion at the OS level surfacing as a non-temporary accept error, a listener closed via an unexpected code path that does not map to net.ErrClosed, or (rare) kernel/driver errors on accept. Normal Ctrl-C/SIGTERM shutdown paths Close the listener and are filtered out, so they do not produce this error.","commonSituations":"Long-running daemons on hosts with tiny ulimit -n where EMFILE eventually becomes non-temporary; container runtimes reaping sockets oddly; custom wrappers closing the listener directly instead of via the provided shutdown; upgrades of Go changing error wrapping so an older binary's is-net.ErrClosed check misses (modern nsq versions match correctly).","solutions":["Check fd limits and usage on the host: 'ulimit -n', 'ls /proc/$(pgrep nsqd)/fd | wc -l'; raise limits (systemd LimitNOFILE) if near the cap.","Look at the wrapped %s text — it names the true cause; act on that syscall error rather than the wrapper.","Restart the daemon; this is a fatal path by design and the data files are safe (nsqd persists its queue).","If it recurs, capture 'dmesg' and Go stack traces, and verify you are not running a very old binary against a newer kernel/socket setup."],"exampleFix":"# before: fd exhaustion surfaces as fatal Serve error at some point\nulimit -n 1024\n# nsqd: http.Serve() error - accept tcp ...: too many open files\n\n# after\n# /etc/systemd/system/nsqd.service\n[Service]\nLimitNOFILE=65536\n# then: systemctl daemon-reload && systemctl restart nsqd","handlingStrategy":"try-catch","validationCode":"// before binding, confirm fd headroom so Serve is unlikely to die of EMFILE\nvar st syscall.Statfs_t\n_ = syscall.Statfs(\"/\", &st)\nsoft, hard := ulimits() // syscall.Getrlimit(RLIMIT_NOFILE)\nif used := countOpenFDs(); soft-used < 1000 {\n    warn(\"raise RLIMIT_NOFILE before serving HTTP\")\n}","typeGuard":null,"tryCatchPattern":"// supervisors: any non-nil return from http_api.Serve is fatal for the daemon;\n// catch, log the wrapped cause, and restart with alerting\nif err := http_api.Serve(ln, mux, \"HTTP\", logf); err != nil && !errors.Is(err, net.ErrClosed) {\n    log.Printf(\"fatal: %v — restarting\", err)\n    notifyOnCall(err)\n    restart()\n}","preventionTips":["Set LimitNOFILE generously on all nsq daemons.","Monitor open-fd counts of long-lived daemons.","Treat this error as a symptom: read the wrapped %s before acting."],"tags":["http","server","lifecycle","fatal","nsqd"],"backgroundTag":null,"analyzedSha":"85cf10c09c6c3c86160d6f0eb156f62d0efc1648","analyzedAt":"2026-08-16T00:53:05.009Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}