caddyserver/caddy · error
invalid socket file descriptor: %d
Error message
invalid socket file descriptor: %d
What it means
Unix-platform counterpart of listen.go:61 — after parsing the fd number, if os.NewFile returns nil (descriptor invalid/not open in this process), Caddy rejects it with the numeric value in the message.
Source
Thrown at listen_unix.go:130
func() {
socketFilesMu.Lock()
defer socketFilesMu.Unlock()
socketFdWide := uintptr(socketFd)
var ok bool
socketFile, ok = socketFiles[socketFdWide]
if !ok {
socketFile = os.NewFile(socketFdWide, lnKey)
if socketFile != nil {
socketFiles[socketFdWide] = socketFile
}
}
}()
if socketFile == nil {
return nil, fmt.Errorf("invalid socket file descriptor: %d", socketFd)
}
} else {
// wrap any Control function set by the user so we can also add our reusePort control without clobbering theirs
oldControl := config.Control
config.Control = func(network, address string, c syscall.RawConn) error {
if oldControl != nil {
if err := oldControl(network, address, c); err != nil {
return err
}
}
return reusePort(network, address, c)
}
}
datagram := slices.Contains([]string{"udp", "udp4", "udp6", "unixgram", "fdgram"}, network)
if datagram {
if fd {
ln, err = net.FilePacketConn(socketFile)View on GitHub (pinned to 50e54ee279)
Solutions
- Check /proc/<caddy-pid>/fd to see which descriptors exist.
- Use systemd socket activation properly and reference the right offset (fds start at 3).
- Prefer 'systemd' integration or plain unix/tcp listeners if fd management is error-prone for you.
Defensive patterns
Strategy: validation
Validate before calling
func fdUsable(fd int) bool {
f := os.NewFile(uintptr(fd), "probe")
if f == nil { return false }
return f != nil // presence in-process implies open
} Prevention
- Pass descriptors explicitly via socket activation rather than guessing indexes.
- After config reloads that close fds, re-derive fd numbers from the environment.
When it happens
Trigger: listen fd N where N is not an open descriptor in the Caddy process on Unix: wrong index for systemd-passed sockets, descriptor already closed by an earlier reload, or fabricated number.
Common situations: systemd socket activation where LISTEN_FDS count changed but the config still references an old index; manually hardcoding 3 when nothing was passed; reload cycles that consumed and closed the cached descriptor.
Related errors
- invalid socket file descriptor: %d
- invalid file descriptor: %v
- invalid file descriptor: %v
- network '%s' cannot handle HTTP/1 or HTTP/2 connections
- parsing browse template: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/ec7e319c26c3e9f7.
Report an issue: GitHub.