{"record":{"id":"73fac88eedaabf5e","repo":"caddyserver/caddy","slug":"invalid-socket-file-descriptor-d","errorCode":null,"errorMessage":"invalid socket file descriptor: %d","messagePattern":"invalid socket file descriptor: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"listen.go","lineNumber":61,"sourceCode":"\t\tfunc() {\n\t\t\tsocketFilesMu.Lock()\n\t\t\tdefer socketFilesMu.Unlock()\n\n\t\t\tsocketFdWide := uintptr(socketFd)\n\t\t\tvar ok bool\n\n\t\t\tsocketFile, ok = socketFiles[socketFdWide]\n\n\t\t\tif !ok {\n\t\t\t\tsocketFile = os.NewFile(socketFdWide, lnKey)\n\t\t\t\tif socketFile != nil {\n\t\t\t\t\tsocketFiles[socketFdWide] = socketFile\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\n\t\tif socketFile == nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid socket file descriptor: %d\", socketFd)\n\t\t}\n\t}\n\n\tdatagram := slices.Contains([]string{\"udp\", \"udp4\", \"udp6\", \"unixgram\", \"fdgram\"}, network)\n\tif datagram {\n\t\tsharedPc, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {\n\t\t\tvar (\n\t\t\t\tpc  net.PacketConn\n\t\t\t\terr error\n\t\t\t)\n\t\t\tif fd {\n\t\t\t\tpc, err = net.FilePacketConn(socketFile)\n\t\t\t} else {\n\t\t\t\tpc, err = config.ListenPacket(ctx, network, address)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/listen.go#L43-L79","documentation":"For 'fd'/'fdgram' listening, Caddy wraps the numeric descriptor with os.NewFile and caches it. If the resulting *os.File is nil (the descriptor could not be turned into a file object), this error reports the offending descriptor number. In practice this signals a descriptor that is not open or not a valid socket in the current process.","triggerScenarios":"listen fd with a number that is not an open descriptor in the Caddy process — e.g. it was closed, belongs to another process, or the value is out of range for open descriptors. os.NewFile returning nil for a bad fd is the trigger.","commonSituations":"Hardcoding an fd number that shifts when the supervisor changes; running Caddy without systemd socket activation while the config assumes it; double-use of a descriptor that was already consumed and closed by an earlier config reload.","solutions":["Confirm the descriptor is actually open in this Caddy process (e.g. ls -l /proc/<pid>/fd).","With systemd, use ListenFDs-style socket activation so the fd is inherited, and reference the correct index.","If not using socket activation, switch to a normal tcp/unix listener instead of 'fd'.","Restart Caddy after changing the supervisor's passed-socket set so numbering matches."],"exampleFix":"// before (fd 3 not actually passed)\n{\n  listen fd 3\n}\n// after\n{\n  listen unix/run/caddy.sock\n}","handlingStrategy":"validation","validationCode":"func fdOpen(fd uint64) bool {\n    _, err := unix.FcntlInt(unix.Fstat(int(fd))) // or os.NewFile check\n    return err == nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["With systemd socket activation, derive fd numbers from LISTEN_FDS/LISTEN_PID env, not constants.","Log open descriptors at startup when debugging fd-based listens."],"tags":["file-descriptor","socket-activation","runtime"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}