valyala/fasthttp · error
prefork: start child %q: %w
Error message
prefork: start child %q: %w
What it means
Returned by Prefork.doCommand when cmd.Start() fails to launch the child process. The library wraps the OS error along with the executable path so the developer knows which binary could not be started. Listener files were already prepared, so the failure is purely in process creation.
Source
Thrown at prefork/prefork.go:380
}
executable, err := os.Executable()
if err != nil {
return nil, fmt.Errorf("prefork: resolve executable: %w", err)
}
args := append([]string{executable}, os.Args[1:]...)
cmd := &exec.Cmd{
Path: executable,
Args: args,
Stdout: os.Stdout,
Stderr: os.Stderr,
Env: childEnv(),
ExtraFiles: p.files,
}
if err = cmd.Start(); err != nil {
return nil, fmt.Errorf("prefork: start child %q: %w", executable, err)
}
return cmd, nil
}
type childExit struct {
err error
pid int
}
// shutdownChildren tears down every entry in childProcs. It first cancels the
// per-child Wait goroutines' context so any parked on a RecoverInterval backoff
// or a sigCh send return immediately; cmd.Wait() is not tied to the context, so
// this only strips the artificial delay from the shutdown path while still
// letting us wait for the children to actually exit. Children are then sent
// SIGTERM (on platforms where it is supported) and given up to grace to exit
// before survivors are killed unconditionally. wg tracks the per-child Wait
// goroutines and is drained before returning so no goroutine outlives prefork().
func (p *Prefork) shutdownChildren(View on GitHub (pinned to c96f600972)
Solutions
- Check the executable exists and is executable (ls -l, chmod +x) at the exact wrapped path.
- Raise process limits: RLIMIT_NPROC (ulimit -u) or container cgroup pids.max.
- Free memory or increase the container memory limit if the cause is EAGAIN/ENOMEM.
- Redeploy/restart after binary updates so parent and child binaries match.
Example fix
// before
# k8s pod hitting pids limit
// after
spec:
containers:
- name: app
resources: { limits: { ... } }
# and raise cgroup pids: securityContext / pids-limit in runtime config Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: check the binary is executable and limits allow fork
if fi, err := os.Stat(os.Args[0]); err != nil || fi.Mode()&0111 == 0 {
log.Fatalf("executable missing or not executable")
}
var rl syscall.Rlimit
syscall.Getrlimit(syscall.RLIMIT_NPROC, &rl) Try / catch
if err := p.Listen(addr); err != nil {
if strings.Contains(err.Error(), "start child") {
var ee *exec.Error
var xe *exec.ExitError
switch {
case errors.As(err, &ee):
log.Fatalf("cannot exec %s: %v", ee.Name, err)
case errors.As(err, &xe):
log.Fatalf("child exited: %v", err)
default:
log.Fatalf("spawn failed (limits/memory?): %v", err)
}
}
} Prevention
- Keep the running binary on disk; restart processes after upgrades.
- Raise pids limits in containers (pids.max / --pids-limit).
- Ensure adequate memory headroom for forking.
- Grant execute permission in your build/packaging pipeline.
When it happens
Trigger: The resolved executable path no longer exists or lacks execute permission; fork/clone fails due to RLIMIT_NPROC/cgroup process limits (e.g. pids.max in containers); memory exhaustion (EAGAIN from fork).
Common situations: Binary removed after an in-place update while preforked parent still runs; Kubernetes/container cgroup hitting the process-count limit; overcommitted host with insufficient memory to fork.
Related errors
- prefork: listen tcp %s: %w
- prefork: dup listener fd: %w
- prefork: command producer: %w
- prefork: resolve executable: %w
- must implement readat
AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31).
Data as JSON: /api/errors/191bca75d4951a2b.
Report an issue: GitHub.