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
- Check and fix permissions on the beads data directory (chown/chmod) and restart.
- Free disk space if the volume is full.
- Remove any directory occupying the PID file's path.
- 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
- Keep the beads dir writable and disk non-full at all times.
- Don't let external tools replace the beads dir during startup.
- After uid changes, chown -R the beads dir.
- Remove stray directories occupying pidfile paths.
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
- write pid file: %w
- multiple .doltcfg directories detected
- failed to walk directory tree: %w
- %d artifact(s) could not be removed
- failed to remove %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c2eef574f80b613d.
Report an issue: GitHub.