gastownhall/beads · error

port %d is in use by another project's dolt server (PID %d).

Error message

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>

What it means

When the explicit port is held by a dolt process, reclaimPort checks whether that process's working directory matches this project's dolt dir (ResolveDoltDir). If it belongs to a different beads project, bd refuses to kill it — it's another project's live server — and errors with the PID and diagnostics instead.

Source

Thrown at internal/doltserver/doltserver.go:448

		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))
}

// portConflictDiagnostics returns a multi-line block of operator-actionable
// hints for diagnosing what's holding a port. Combines the platform-specific
// listener-discovery command with a docker-in-the-loop hint that frequently
// applies in practice — operators running their own dolt sql-server in a
// container don't realize bd would otherwise try to start a competing
// instance and lose the race (GH#3516).
func portConflictDiagnostics(port int) string {
	return fmt.Sprintf("Identify the listener:\n  %s\n\n"+
		"If the listener is YOUR own Dolt instance (e.g., a docker container "+
		"or systemd unit you manage), bd does not need to start a new server. "+
		"Configure bd to talk to the existing server instead:\n"+
		"  export BEADS_DOLT_SERVER_HOST=<host>  # 127.0.0.1 for local container\n"+
		"  export BEADS_DOLT_SERVER_PORT=%d\n"+
		"  bd dolt status   # verify reachable",
		fmt.Sprintf(portConflictHint, port), port)
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Give each project its own port: bd dolt set port <unique-port> in the second project
  2. Remove the fixed port (use shared-server mode) so projects share one server: ~/.beads/shared-server
  3. Stop the other project's server (bd dolt stop in that project) if you no longer need it
  4. Use BEADS_DOLT_SERVER_HOST/PORT to connect to the other server only if it serves the data you need

Example fix

// before: both projects pinned to the same port
# project A and B both: bd dolt set port 31145
// after: per-project ports
# project A: bd dolt set port 31145
# project B: bd dolt set port 31146
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the fixed port is unique per project before start
cfgPort := 31145 // read from config
out, _ := exec.Command("sh", "-c", fmt.Sprintf("lsof -ti :%d | xargs -r ps -o cmd= -p", cfgPort)).Output()
if strings.Contains(string(out), "dolt sql-server") && !strings.Contains(string(out), os.Getenv("PWD")) {
    fmt.Println("port held by another project's dolt server; choose a different port")
}

Try / catch

_, err := doltserver.Start(beadsDir)
if err != nil && strings.Contains(err.Error(), "another project's dolt server") {
    // reallocate this project to a unique port and retry
    exec.Command("bd", "dolt", "set", "port", "31146").Run()
    _, err = doltserver.Start(beadsDir)
}

Prevention

When it happens

Trigger: Start() with an explicit port that is already bound by another beads project's dolt sql-server (a dolt process whose CWD is not this project's dolt data directory), so isProcessInDir(pid, doltDir) is false.

Common situations: Multiple bd checkouts sharing one fixed port in config.yaml or BEADS_DOLT_SERVER_PORT in the shell environment, where whichever project started first owns the port.

Related errors


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