valyala/fasthttp · error

prefork: dup listener fd: %w

Error message

prefork: dup listener fd: %w

What it means

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.

Source

Thrown at prefork/prefork.go:323

	}

	tcpAddr, err := net.ResolveTCPAddr(p.Network, addr)
	if err != nil {
		return fmt.Errorf("prefork: resolve %s/%s: %w", p.Network, addr, err)
	}

	tcpListener, err := net.ListenTCP(p.Network, tcpAddr)
	if err != nil {
		return fmt.Errorf("prefork: listen tcp %s: %w", addr, err)
	}

	listenerFile, err := tcpListenerFile(tcpListener)
	if err != nil {
		// Close the bound listener so we don't leak the socket/fd when
		// File() fails. p.ln is intentionally only assigned after this
		// point so the caller never sees a half-initialised state.
		_ = tcpListener.Close()
		return fmt.Errorf("prefork: dup listener fd: %w", err)
	}

	p.ln = tcpListener
	p.files = []*os.File{listenerFile}

	return nil
}

// childEnv returns os.Environ() with the prefork child marker variable set,
// stripping any pre-existing value to avoid duplicate keys with last-wins
// semantics.
func childEnv() []string {
	src := os.Environ()
	out := make([]string, 0, len(src)+1)
	prefix := preforkChildEnvVariable + "="
	for _, kv := range src {
		if len(kv) >= len(prefix) && kv[:len(prefix)] == prefix {
			continue

View on GitHub (pinned to c96f600972)

Solutions

  1. Raise the file-descriptor limit: ulimit -n or systemd LimitNOFILE.
  2. Check for fd leaks in the process (lsof -p <pid> | wc -l).
  3. Retry after verifying the environment allows fcntl(F_DUPFD_CLOEXEC); inspect the wrapped error for the exact syscall failure.
  4. Update the library if using an OS/environment with known File() quirks on the runtime version.

Example fix

// before
# container with default low limit
// after
# docker run --ulimit nofile=65536:65536 ...
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: ensure the process can allocate fds
var lim syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim); err != nil || lim.Cur < 1024 {
    log.Printf("warning: low RLIMIT_NOFILE: %v (cur=%d)", err, lim.Cur)
}

Try / catch

if err := p.Listen(addr); err != nil {
    if strings.Contains(err.Error(), "dup listener fd") {
        log.Fatalf("fd duplication failed (check ulimit -n): %v", err)
    }
    log.Fatalf("prefork failed: %v", err)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31). Data as JSON: /api/errors/c28b5a3d69def4df. Report an issue: GitHub.