{"record":{"id":"c28b5a3d69def4df","repo":"valyala/fasthttp","slug":"prefork-dup-listener-fd-w","errorCode":null,"errorMessage":"prefork: dup listener fd: %w","messagePattern":"prefork: dup listener fd: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"prefork/prefork.go","lineNumber":323,"sourceCode":"\t}\n\n\ttcpAddr, err := net.ResolveTCPAddr(p.Network, addr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"prefork: resolve %s/%s: %w\", p.Network, addr, err)\n\t}\n\n\ttcpListener, err := net.ListenTCP(p.Network, tcpAddr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"prefork: listen tcp %s: %w\", addr, err)\n\t}\n\n\tlistenerFile, err := tcpListenerFile(tcpListener)\n\tif err != nil {\n\t\t// Close the bound listener so we don't leak the socket/fd when\n\t\t// File() fails. p.ln is intentionally only assigned after this\n\t\t// point so the caller never sees a half-initialised state.\n\t\t_ = tcpListener.Close()\n\t\treturn fmt.Errorf(\"prefork: dup listener fd: %w\", err)\n\t}\n\n\tp.ln = tcpListener\n\tp.files = []*os.File{listenerFile}\n\n\treturn nil\n}\n\n// childEnv returns os.Environ() with the prefork child marker variable set,\n// stripping any pre-existing value to avoid duplicate keys with last-wins\n// semantics.\nfunc childEnv() []string {\n\tsrc := os.Environ()\n\tout := make([]string, 0, len(src)+1)\n\tprefix := preforkChildEnvVariable + \"=\"\n\tfor _, kv := range src {\n\t\tif len(kv) >= len(prefix) && kv[:len(prefix)] == prefix {\n\t\t\tcontinue","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/valyala/fasthttp/blob/c96f600972c6f4a7a30d664257b340ebe9d60124/prefork/prefork.go#L305-L341","documentation":"Returned by Prefork.setTCPListenerFiles when tcpListenerFile (which calls the listener's File() to duplicate the socket fd) fails. The bound listener is explicitly closed first so no socket/fd leaks. This means the OS refused to duplicate the listener's file descriptor, which prefork needs to pass to child processes.","triggerScenarios":"net.TCPListener.File() returning an error — typically when the underlying fd is invalid/closed, or resource limits (RLIMIT_NOFILE) prevent creating a new fd. Triggered inside prefork setup after a successful ListenTCP.","commonSituations":"File-descriptor exhaustion on the host (ulimit -n too low for the app's open files/sockets); running in restricted environments (some sandboxes/seccomp setups) where dup fcntl calls are blocked.","solutions":["Raise the file-descriptor limit: ulimit -n or systemd LimitNOFILE.","Check for fd leaks in the process (lsof -p <pid> | wc -l).","Retry after verifying the environment allows fcntl(F_DUPFD_CLOEXEC); inspect the wrapped error for the exact syscall failure.","Update the library if using an OS/environment with known File() quirks on the runtime version."],"exampleFix":"// before\n# container with default low limit\n// after\n# docker run --ulimit nofile=65536:65536 ...","handlingStrategy":"validation","validationCode":"// Pre-flight: ensure the process can allocate fds\nvar lim syscall.Rlimit\nif err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil || lim.Cur < 1024 {\n    log.Printf(\"warning: low RLIMIT_NOFILE: %v (cur=%d)\", err, lim.Cur)\n}","typeGuard":null,"tryCatchPattern":"if err := p.Listen(addr); err != nil {\n    if strings.Contains(err.Error(), \"dup listener fd\") {\n        log.Fatalf(\"fd duplication failed (check ulimit -n): %v\", err)\n    }\n    log.Fatalf(\"prefork failed: %v\", err)\n}","preventionTips":["Set generous RLIMIT_NOFILE in systemd/Docker (nofile=65536).","Monitor open fd counts in production.","Avoid running under sandboxes that block fcntl duplication.","Test prefork startup in a staging environment matching production limits."],"tags":["go","prefork","file-descriptor"],"backgroundTag":"fd-exhaustion","analyzedSha":"c96f600972c6f4a7a30d664257b340ebe9d60124","analyzedAt":"2026-08-31T22:48:28.265Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}