gastownhall/beads · error

writing PID file: %w

Error message

writing PID file: %w

What it means

After a successful spawn, Start() persists the child's PID to pidPath(beadsDir). If that write fails, Start kills the freshly started process (to avoid an untracked orphan) and wraps the error as 'writing PID file'. The subsequent port-file write has the same kill-and-rollback behavior.

Source

Thrown at internal/doltserver/doltserver.go:1485

			// detected here but never auto-repaired — reinitializing .dolt is
			// destructive, so repair stays behind explicit bd doctor --fix.
			if dirs, detErr := detectCorruptManifest(beadsDir, doltDir); detErr == nil && len(dirs) > 0 {
				return nil, fmt.Errorf("failed to start dolt server after %d attempts: %w\n"+
					"Corrupt manifest with no recoverable data detected (GH#3290) in:\n  %s\n"+
					"Run 'bd doctor --fix' to back up the corrupt database(s) and reinitialize.\nCheck logs: %s",
					attempts, lastErr, strings.Join(dirs, "\n  "), logPath(beadsDir))
			}
			return nil, fmt.Errorf("failed to start dolt server after %d attempts: %w\nCheck logs: %s",
				attempts, lastErr, logPath(beadsDir))
		}
	}

	// Write PID and port files
	if err := os.WriteFile(pidPath(beadsDir), []byte(strconv.Itoa(pid)), 0600); err != nil {
		if proc, findErr := os.FindProcess(pid); findErr == nil {
			_ = proc.Kill()
		}
		return nil, fmt.Errorf("writing PID file: %w", err)
	}
	if err := writePortFile(beadsDir, actualPort); err != nil {
		if proc, findErr := os.FindProcess(pid); findErr == nil {
			_ = proc.Kill()
		}
		_ = os.Remove(pidPath(beadsDir))
		return nil, fmt.Errorf("writing port file: %w", err)
	}

	// Wait for server to accept connections
	if err := waitForReady(cfg.Host, actualPort, readyTimeout()); err != nil {
		if proc, findErr := os.FindProcess(pid); findErr == nil {
			_ = proc.Kill()
		}
		_ = os.Remove(pidPath(beadsDir))
		_ = os.Remove(portPath(beadsDir))
		if hasJournalCorruption, logErr := logHasCorruptJournalError(logPath(beadsDir)); logErr == nil && hasJournalCorruption {
			return nil, fmt.Errorf("server started (PID %d) but not accepting connections on port %d: %w\n\n%s",

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check and fix permissions on the beads data directory (chown/chmod) and restart.
  2. Free disk space if the volume is full.
  3. Remove any directory occupying the PID file's path.
  4. Verify no sync/backup tool is flipping the directory read-only during startup.

Example fix

// before
ls -ld ~/.beads  # owned by root
bd start  # writing PID file: permission denied (server killed)
// after
sudo chown -R "$USER" ~/.beads
bd start
Defensive patterns

Strategy: validation

Validate before calling

await fs.mkdir(dataDir, { recursive: true });
await fs.access(dataDir, fs.constants.W_OK);
const pidPath = path.join(dataDir, 'dolt.pid');
try { const s = await fs.stat(pidPath); if (s.isDirectory()) throw new Error('pid path is a directory'); }
catch (e) { if (e.code !== 'ENOENT') throw e; }

Type guard

null

Try / catch

try {
  await bdStart();
} catch (e) {
  if (/writing PID file/.test(e.message)) {
    // server was killed to avoid an orphan; fix dir perms/space, then restart
  }
  throw e;
}

Prevention

When it happens

Trigger: os.WriteFile(pidPath(beadsDir), pid, 0600) fails right after a successful server launch — unwritable beads dir, disk full, or path collision.

Common situations: Beads dir permissions changed while the server was starting; disk filled during startup; another process recreated the beads dir as a read-only mount; security software blocking new files.

Related errors


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