gofiber/fiber · critical
prefork
Error message
prefork: %w
What it means
Thrown when the prefork master process's ListenAndServe(addr) fails. Prefork uses SO_REUSEPORT to run multiple child listeners; the master must itself bind the address. The %w wraps the underlying listener/bind error. This is fatal to server startup — if the master cannot listen, no children serve.
Solutions
- Free or change the port (the wrapped error is usually EADDRINUSE / EACCES — read it).
- Run with privileges for ports <1024, or use a non-privileged port plus setcap/sudo/iptables redirect.
- Disable prefork on platforms without SO_REUSEPORT support.
- Validate the address format and ListenerNetwork before calling Listen with Prefork: true.
- Check the container/host port binding.
Example fix
// before
app.Listen(":80", fiber.ListenConfig{Prefork: true}) // EACCES or EADDRINUSE -> prefork: ...
// after
addr := ":8080"
ln, err := net.Listen("tcp", addr)
if err != nil { log.Fatalf("cannot bind %s: %v", addr, err) }
ln.Close()
app.Listen(addr, fiber.ListenConfig{Prefork: true}) Defensive patterns
Strategy: validation
Validate before calling
// Probe-bind before starting prefork to get a clear, early failure.
func checkListen(addr, network string) error {
ln, err := net.Listen(network, addr)
if err != nil { return err }
return ln.Close()
}
// also guard privileges for low ports
func canBindLowPort(port int) bool {
return port >= 1024 || os.Geteuid() == 0
} Type guard
func addrLikelyBindable(addr string) bool {
_, portStr, err := net.SplitHostPort(addr)
if err != nil { return false }
port, _ := strconv.Atoi(portStr)
return port >= 1024 || os.Geteuid() == 0
} Try / catch
if err := checkListen(addr, cfg.ListenerNetwork); err != nil {
log.Fatalf("prefork pre-bind check failed for %s: %v", addr, err)
}
if err := app.Listen(addr, fiber.ListenConfig{Prefork: true}); err != nil {
log.Fatalf("prefork: %v", err)
} Prevention
- Probe-bind the address before starting prefork to surface EADDRINUSE/EACCES early.
- Avoid privileged ports unless the process has CAP_NET_BIND_SERVICE / runs as root.
- Do not enable Prefork on platforms without SO_REUSEPORT.
- Confirm the container/host port is free.
When it happens
Trigger: The address is already bound by another process (EADDRINUSE); binding a privileged port (<1024) without sufficient privileges (EACCES); SO_REUSEPORT unsupported on the platform/older kernel; an invalid/already-resolved address; listener creation failure; the network value in ListenConfig is invalid.
Common situations: Another server already on the port; running as non-root on :80/:443; older Windows or kernels without SO_REUSEPORT; misformed host:port in app.Listen(..., prefork); running inside a container whose port is already bound at the host level.
Related errors
- failed to listen
- basicauth: charset must be UTF-8
- cache: failed to delete key
- cache: failed to get key
- cache: failed to get raw key
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/abd0b523081de13a.
Report an issue: GitHub.
Appendix: source
Thrown at prefork.go:116
}
return nil
}
// Master callback: all children spawned → startup message & OnListen hooks
p.OnMasterReady = func(childPIDs []int) error {
listenData := app.prepareListenData(addr, tlsConfig != nil, cfg, childPIDs)
app.runOnListenHooks(listenData)
app.printMessages(cfg, listenData)
return nil
}
// Master callback: child recovered after crash
p.OnChildRecover = func(oldPID, newPID int) {
logger.Printf("prefork: child %d crashed, recovered with new PID %d", oldPID, newPID)
}
if err := p.ListenAndServe(addr); err != nil {
return fmt.Errorf("prefork: %w", err)
}
return nil
}
// dummyCmd is for internal prefork testing
func dummyCmd() *exec.Cmd {
command := "go"
if storeCommand := dummyChildCmd.Load(); storeCommand != nil && storeCommand != "" {
command = storeCommand.(string) //nolint:forcetypeassert,errcheck // We always store a string in here
}
if runtime.GOOS == windowsOS {
return exec.Command("cmd", "/C", command, "version")
}
return exec.Command(command, "version")
}
View on GitHub (pinned to a105acad6c)