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

  1. Check /proc/<caddy-pid>/fd to see which descriptors exist.
  2. Use systemd socket activation properly and reference the right offset (fds start at 3).
  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

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


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/ec7e319c26c3e9f7. Report an issue: GitHub.