gastownhall/beads · error

cannot start dolt server on port %d: %w

Error message

cannot start dolt server on port %d: %w

What it means

For an explicit (non-ephemeral) port, Start() calls reclaimPort(host, port, beadsDir) to detect conflicts and adopt an already-running server on that port. If reclaimPort returns an error, the log file is closed and Start fails with 'cannot start dolt server on port %d'.

Source

Thrown at internal/doltserver/doltserver.go:1361

		// Open log file
		logFile, err := os.OpenFile(logPath(beadsDir), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600) //nolint:gosec // G304: logPath derives from user-configured beadsDir
		if err != nil {
			return nil, fmt.Errorf("opening log file: %w", err)
		}

		// Resolve the port to use. Explicit ports (env/config) go through
		// reclaimPort for conflict detection. Port 0 means ephemeral — allocate
		// a fresh port from the OS with retry for TOCTOU races.
		actualPort = cfg.Port
		explicitPort := actualPort > 0

		if explicitPort {
			// Explicit port: check for conflicts and adopt existing servers.
			adoptPID, reclaimErr := reclaimPort(cfg.Host, actualPort, beadsDir)
			if reclaimErr != nil {
				_ = logFile.Close()
				return nil, fmt.Errorf("cannot start dolt server on port %d: %w", actualPort, reclaimErr)
			}
			if adoptPID > 0 {
				_ = logFile.Close()
				_ = os.WriteFile(pidPath(beadsDir), []byte(strconv.Itoa(adoptPID)), 0600)
				_ = writePortFile(beadsDir, actualPort)
				return &State{Running: true, PID: adoptPID, Port: actualPort, DataDir: doltDir}, nil
			}
		}

		// Start dolt sql-server, with retry loop for ephemeral port TOCTOU.
		pid = 0
		lastErr = nil
		attempts = 1
		if !explicitPort {
			attempts = maxEphemeralPortAttempts
		}

		for i := range attempts {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Find what holds the port (lsof -i :PORT or ss -ltnp) and stop the conflicting process if it is safe.
  2. If it is another bd/dolt instance on this data dir, remove stale PID/port files in the beads dir and retry.
  3. Change the configured port to a free one in beads config or the environment variable.
  4. Verify reclaimPort's OS probes can run (permissions for lsof/ps, /proc access in containers).

Example fix

// before: port 4141 held by a stale process
ss -ltnp | grep 4141
kill <pid>
// after: bd starts cleanly, or set BD_PORT=0 for an ephemeral port
Defensive patterns

Strategy: try-catch

Validate before calling

// probe the explicit port before starting
const { createServer } = require('net');
const s = createServer();
s.once('error', e => console.error('port in use:', e.code));
s.listen(port, host, () => { s.close(); });

Type guard

null

Try / catch

try {
  await bdStart();
} catch (e) {
  const m = e.message.match(/cannot start dolt server on port (\d+)/);
  if (m) { /* free the port or pick another, then retry */ }
  throw e;
}

Prevention

When it happens

Trigger: Explicit port configured (env/config, not port 0) and reclaimPort fails while probing/reclaiming that port.

Common situations: Port held by a foreign process that cannot be adopted; stale PID/port files pointing at unrelated processes; permission errors inspecting /proc or lsof; another app bound to the configured port.

Related errors


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