gastownhall/beads · warning

finding process %d: %w

Error message

finding process %d: %w

What it means

gracefulStop wraps the error from os.FindProcess(pid) as "finding process %d: %w". os.FindProcess on Unix only fails if the pid is invalid or the OS lookup errors (it does NOT verify the process exists); on Windows it opens a handle and fails if the PID is dead. This library throws it when it cannot obtain a handle to the dolt-server process it is trying to stop gracefully (SIGTERM, then SIGKILL) during reclaimPort or StopWithForce.

Source

Thrown at internal/doltserver/doltserver_unix.go:142

	return false
}

// isProcessAlive checks if a process with the given PID is running.
// Uses signal 0 which doesn't send a signal but checks process existence.
func isProcessAlive(pid int) bool {
	process, err := os.FindProcess(pid)
	if err != nil {
		return false
	}
	return process.Signal(syscall.Signal(0)) == nil
}

// gracefulStop sends SIGTERM, waits for the process to exit, then SIGKILL if needed.
// Used by reclaimPort and StopWithForce where data has already been flushed.
func gracefulStop(pid int, timeout time.Duration) error {
	process, err := os.FindProcess(pid)
	if err != nil {
		return fmt.Errorf("finding process %d: %w", pid, err)
	}

	if err := process.Signal(syscall.SIGTERM); err != nil {
		return fmt.Errorf("sending SIGTERM to PID %d: %w", pid, err)
	}

	// Poll for exit
	deadline := time.Now().Add(timeout)
	for time.Now().Before(deadline) {
		time.Sleep(500 * time.Millisecond)
		if process.Signal(syscall.Signal(0)) != nil {
			return nil // exited
		}
	}

	// Still running — force kill
	_ = process.Signal(syscall.SIGKILL)
	time.Sleep(100 * time.Millisecond)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the pid being passed is the valid, current dolt-server PID from server metadata; re-read metadata in case it is stale.
  2. Treat 'process already done' (os.ErrProcessDone) as success — the goal was a stopped server; retry the port bind instead of failing.
  3. On Unix, verify liveness with signal 0 (process.Signal(syscall.Signal(0))) before assuming the stop failed.
  4. Log and continue: if the process is gone, the port is likely free; proceed with startup.

Example fix

// before
process, err := os.FindProcess(pid)
if err != nil {
    return fmt.Errorf("finding process %d: %w", pid, err)
}
// after
process, err := os.FindProcess(pid)
if err != nil {
    if pid <= 0 {
        return fmt.Errorf("finding process %d: %w", pid, err)
    }
    // already-gone: treat as stopped
    return nil
}
Defensive patterns

Strategy: try-catch

Validate before calling

if pid <= 0 {
    return fmt.Errorf("invalid pid %d; refresh server metadata", pid)
}
if p, err := os.FindProcess(pid); err == nil {
    if err := p.Signal(syscall.Signal(0)); err != nil {
        // process already gone; no stop needed
    }
}

Type guard

func pidLooksAlive(pid int) bool {
    if pid <= 0 { return false }
    p, err := os.FindProcess(pid)
    if err != nil { return false }
    return p.Signal(syscall.Signal(0)) == nil
}

Try / catch

err := gracefulStop(pid, timeout)
if err != nil {
    if errors.Is(err, os.ErrProcessDone) {
        return nil // already stopped
    }
    return fmt.Errorf("graceful stop pid %d: %w", pid, err)
}

Prevention

When it happens

Trigger: Calling gracefulStop (via reclaimPort or StopWithForce) with a pid <= 0 or a stale pid whose process already exited (Windows), or an OS-level failure in process lookup.

Common situations: A dolt server recorded in the metadata file died on its own before bd tried to stop it; a recycled/stale PID in stored server metadata; passing a zero or negative PID from a failed earlier parse.

Related errors


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