gastownhall/beads · error

port %d is in use by a non-dolt process (PID %d).\n\n%s\n\nF

Error message

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>

What it means

reclaimPort identifies the process holding an explicit port and refuses to proceed if it is not a dolt sql-server. Killing or stealing the port of an unrelated process would be destructive, so bd fails fast with the offending PID and operator diagnostics, advising a port change or freeing the port.

Source

Thrown at internal/doltserver/doltserver.go:435

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

// 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

View on GitHub (pinned to 71377f2769)

Solutions

  1. Pick another port: bd dolt set port <port>
  2. If the holder is your own Dolt instance (e.g. docker container), don't start a local server — point bd at it: export BEADS_DOLT_SERVER_HOST=127.0.0.1 and BEADS_DOLT_SERVER_PORT=<port>
  3. Stop the conflicting process identified by the PID/diagnostics command
  4. Follow the portConflictDiagnostics block (lsof/ss command) to confirm what is listening

Example fix

// before: config.yaml pins a port already used by a dev web server
port: 8080
// after: choose an unused port
# bd dolt set port 31145
port: 31145
Defensive patterns

Strategy: validation

Validate before calling

// Before configuring an explicit port, ensure nothing non-dolt holds it
out, _ := exec.Command("lsof", "-ti", ":8080").Output()
if len(out) > 0 {
    fmt.Println("port 8080 already in use; pick another")
}

Try / catch

_, err := doltserver.Start(beadsDir)
if err != nil && strings.Contains(err.Error(), "non-dolt process") {
    // switch to a free port programmatically
    exec.Command("bd", "dolt", "set", "port", "31145").Run()
    _, err = doltserver.Start(beadsDir)
}

Prevention

When it happens

Trigger: Start() with an explicit port where the port is bound by a non-dolt process (web server, another database, docker-proxy, IDE debugger) — findPIDOnPort returns a PID but isDoltProcess(pid) is false.

Common situations: Two projects on one machine hard-coding the same port in config.yaml, a locally running Postgres/MySQL/dev server already on the chosen port, docker publishing a container port onto the same number.

Related errors


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