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
- Give each project its own port: bd dolt set port <unique-port> in the second project
- Remove the fixed port (use shared-server mode) so projects share one server: ~/.beads/shared-server
- Stop the other project's server (bd dolt stop in that project) if you no longer need it
- 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
- Give every project its own explicit port, or use shared-server mode instead
- Never export a single BEADS_DOLT_SERVER_PORT globally across projects
- Adopt shared-server mode (~/.beads/shared-server) for multi-project machines
- Document each project's port allocation to avoid accidental reuse
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
- port %d is busy but cannot identify the process.\n\nCheck wi
- port %d is in use by a non-dolt process (PID %d).\n\n%s\n\nF
- dolt sql-server exited before listener became ready
- failed to remove backup: %w
- server not reachable: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/eceed34f8ef7032d.
Report an issue: GitHub.