gastownhall/beads · error

port %d is busy but cannot identify the process.\n\nCheck wi

Error message

port %d is busy but cannot identify the process.\n\nCheck with: %s

What it means

reclaimPort validates a user-configured (explicit) port before Start launches dolt sql-server. When the port is busy but findPIDOnPort returns 0 (the listener's process cannot be identified — common with TIME_WAIT sockets or other users' processes), it waits 2 seconds and retries; if still busy it throws this error because bd cannot decide whether the port is safe to reclaim.

Source

Thrown at internal/doltserver/doltserver.go:430

//
// Returns (adoptPID, nil) when an existing server should be adopted.
// Returns (0, nil) when the port is free for a new server.
// Returns (0, err) when the port can't be used.
func reclaimPort(host string, port int, beadsDir string) (adoptPID int, err error) {
	if isPortAvailable(host, port) {
		return 0, nil // port is free
	}

	// Port is busy — find out what's using it
	pid := findPIDOnPort(port)
	if pid == 0 {
		// Can't identify the process; port may be in TIME_WAIT or transient use.
		// Wait briefly and retry.
		time.Sleep(2 * time.Second)
		if isPortAvailable(host, port) {
			return 0, nil
		}
		return 0, fmt.Errorf("port %d is busy but cannot identify the process.\n\nCheck with: %s", port, fmt.Sprintf(portConflictHint, port))
	}

	// Check if it's a dolt sql-server process
	if !isDoltProcess(pid) {
		return 0, fmt.Errorf("port %d is in use by a non-dolt process (PID %d).\n\n%s\n\nFree the port or configure a different one with: bd dolt set port <port>", port, pid, portConflictDiagnostics(port))
	}

	// It's a dolt process. Check if it's one we should adopt.

	// Check if the process is using our data directory (CWD matches our dolt dir).
	// dolt sql-server is started with cmd.Dir = doltDir, so CWD is the data dir.
	doltDir := ResolveDoltDir(beadsDir)
	if isProcessInDir(pid, doltDir) {
		return pid, nil // our server — adopt it
	}

	// Another beads project's Dolt server is on this port.
	return 0, fmt.Errorf("port %d is in use by another project's dolt server (PID %d).\n\n%s\n\nFree the port or use a different one with: bd dolt set port <port>", port, pid, portConflictDiagnostics(port))

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run the diagnostics command in the message (platform-specific lsof/ss command) to find the listener
  2. Wait a few minutes for TIME_WAIT sockets to expire and retry bd
  3. Set SO_REUSEADDR-equivalent behavior by simply retrying bd start after a short delay
  4. If the listener is a foreign container/service, either stop it or point bd at it via BEADS_DOLT_SERVER_HOST/PORT instead of reclaiming
  5. Choose a different explicit port with: bd dolt set port <port>

Example fix

// before: fixed port collides with a TIME_WAIT socket
port := 8001
// after: let bd pick an ephemeral port by clearing the explicit port config
// bd dolt set port 0  (or remove the port from config.yaml)
Defensive patterns

Strategy: retry

Validate before calling

// Check port availability before starting
import "net"
func portFree(host string, port int) bool {
    ln, err := net.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(port)))
    if err != nil { return false }
    ln.Close()
    return true
}

Try / catch

_, err := doltserver.Start(beadsDir)
if err != nil && strings.Contains(err.Error(), "cannot identify the process") {
    time.Sleep(5 * time.Second) // allow TIME_WAIT to clear
    _, err = doltserver.Start(beadsDir)
}

Prevention

When it happens

Trigger: Start() with an explicit port where isPortAvailable fails, no owning PID is discoverable (e.g. socket in TIME_WAIT, listener owned by another user/namespace), and the port remains unavailable after the 2-second retry.

Common situations: Restarting bd immediately after killing a previous server (TIME_WAIT lingering), Docker/NAT setups where the listener lives in another network namespace, containers where lsof/ss lack permission to map the port to a PID.

Related errors


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