valyala/fasthttp · error

prefork: commandproducer must return a started command

Error message

prefork: commandproducer must return a started command

What it means

ErrCommandProducerNotStarted is returned when a CommandProducer returns an *exec.Cmd whose Process is nil, which means cmd.Start() was never called. Prefork expects the producer to hand back an already-started command; merely constructing one with exec.Command is insufficient.

Source

Thrown at prefork/prefork.go:59

	// tcpListenerFile is a hook for (*net.TCPListener).File so tests can
	// inject failure paths without binding a real socket.
	tcpListenerFile = (*net.TCPListener).File

	// ErrOverRecovery is returned when child prefork process restarts exceed
	// the value of RecoverThreshold.
	ErrOverRecovery = errors.New("prefork: exceeding the value of recoverthreshold")

	// ErrOnlyReuseportOnWindows is returned when running on Windows without Reuseport.
	ErrOnlyReuseportOnWindows = errors.New("prefork: windows only supports reuseport = true")

	// ErrCommandProducerNilCmd is returned when a CommandProducer returns
	// (nil, nil) instead of a started command.
	ErrCommandProducerNilCmd = errors.New("prefork: commandproducer returned nil command")

	// ErrCommandProducerNotStarted is returned when a CommandProducer returns
	// an *exec.Cmd whose Process is nil (i.e. cmd.Start() was not called).
	ErrCommandProducerNotStarted = errors.New("prefork: commandproducer must return a started command")
)

// Logger is used for logging formatted messages. Its method set is intentionally
// identical to fasthttp.Logger so that *fasthttp.Server.Logger can be assigned
// directly.
type Logger interface {
	// Printf must have the same semantics as log.Printf.
	Printf(format string, args ...any)
}

// Compile-time check that fasthttp.Logger satisfies the local Logger interface;
// keeps the two types in sync if either side ever evolves.
var _ Logger = fasthttp.Logger(nil)

// Prefork implements fasthttp server prefork.
//
// Preforks master process (with all cores) between several child processes
// increases performance significantly, because Go doesn't have to share

View on GitHub (pinned to c96f600972)

Solutions

  1. Call cmd.Start() inside the CommandProducer before returning it (or return the error from Start()).
  2. If you cannot start it yourself, use exec.Cmd only via producers that guarantee Start() was invoked.
  3. Check cmd.Process != nil in tests for your producer.

Example fix

// before
producer := func() (*exec.Cmd, error) {
    return exec.Command("app")
}
// after
producer := func() (*exec.Cmd, error) {
    cmd := exec.Command("app")
    if err := cmd.Start(); err != nil { return nil, err }
    return cmd, nil
}
Defensive patterns

Strategy: validation

Validate before calling

cmd, err := producer()
if err == nil && (cmd == nil || cmd.Process == nil) {
    return errors.New("commandproducer must return a started command")
}

Type guard

func isStarted(cmd *exec.Cmd) bool {
    return cmd != nil && cmd.Process != nil
}

Prevention

When it happens

Trigger: A CommandProducer returns `exec.Command("app", args...)` directly without calling `.Start()` on it, then prefork's doCommand validates `cmd.Process == nil` and fails.

Common situations: Implementations that confuse exec.Command (which only builds the Cmd) with exec.Start; refactors that removed the Start() call while keeping the return.

Related errors


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