gastownhall/beads · error

fork child: %w

Error message

fork child: %w

What it means

The library failed to fork/exec the proxy child process (forkExecChild). This wraps the OS-level spawn failure — binary missing, exec format or permission problem, or resource limits — after the lock and quarantine steps already succeeded. Everything spawned so far is cleaned up (handoff flag, marker, lock) before returning.

Source

Thrown at internal/storage/dbproxy/proxy/endpoint.go:338

	currentEpoch, err := readStopEpoch(rootDir)
	if err != nil {
		return Endpoint{}, fmt.Errorf("read proxy stop epoch under lock: %w", err)
	}
	if currentEpoch != stopEpoch {
		return Endpoint{}, fmt.Errorf("%w for %s", errStartInterrupted, rootDir)
	}

	if err := quarantineForSpawn(rootDir, discovery); err != nil {
		return Endpoint{}, err
	}
	if err := cleanupOrphanBackend(rootDir); err != nil {
		return Endpoint{}, err
	}

	handedOff = true
	child, err := forkExecChild(rootDir, opts, opts.Port, stopEpoch, lock)
	if err != nil {
		return Endpoint{}, fmt.Errorf("fork child: %w", err)
	}
	defer func() { _ = clearOwnSpawnMarker(rootDir, child.marker) }()
	defer func() {
		if child.handle != nil {
			_ = child.handle.Close()
		}
	}()

	hard := time.NewTimer(spawnReadyHardTimeout)
	defer hard.Stop()
	poll := time.NewTicker(openPollInterval)
	defer poll.Stop()

	for {
		discovered := readAndDial(rootDir)
		if discovered.status == adoptionAdopted {
			if err := sweepOldQuarantines(rootDir, time.Now()); err != nil {
				log.Printf("dbproxy: could not sweep old quarantined records in %s: %v", rootDir, err)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the configured binary path exists and is executable: ls -l $(DoltBinPath) and try running it directly
  2. Check the mount is not noexec (mount output) and fix the binary location
  3. Raise limits (ulimit -u, cgroup pids.max) if fork fails under load
  4. Reinstall/upgrade bd so the bundled paths match the current version

Example fix

// before
opts.DoltBinPath = "/usr/local/bin/dolt" // binary removed by upgrade
// error: fork child: fork/exec /usr/local/bin/dolt: no such file or directory
// after
if _, err := os.Stat(opts.DoltBinPath); err != nil {
    opts.DoltBinPath = "/opt/bd/bin/dolt" // re-point at the real binary
}
ep, err := proxy.GetCreateDatabaseProxyServerEndpoint(root, opts)
Defensive patterns

Strategy: validation

Validate before calling

// Verify the spawn binary before opening
if opts.DoltBinPath != "" {
    if fi, err := os.Stat(opts.DoltBinPath); err != nil || fi.IsDir() || fi.Mode().Perm()&0o100 == 0 {
        return fmt.Errorf("spawn binary missing or not executable: %s", opts.DoltBinPath)
    }
}

Prevention

When it happens

Trigger: forkExecChild(rootDir, opts, opts.Port, stopEpoch, lock) fails during spawnAndHandoff: opts.DoltBinPath (or the proxy entrypoint binary) does not exist or is not executable, exec fails due to ENOEXEC/EACCES, or fork hits resource limits (ulimit, PID exhaustion).

Common situations: bd upgraded/downgraded and the configured DoltBinPath points to an old or removed binary; container images missing the dolt binary; noexec mount on the binary's filesystem; hitting process limits in CI sandboxes.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/5e769013b5f86ade. Report an issue: GitHub.