gastownhall/beads · error

reading PID file: %w

Error message

reading PID file: %w

What it means

IsRunning reads the bd PID file for the given beads dir to report server state. If os.ReadFile fails with anything other than NotExist (permission denied, is-a-directory, I/O error), the read error is wrapped as this error. A missing PID file is normal (returns Running:false); an unreadable one is not.

Source

Thrown at internal/doltserver/doltserver.go:876

			if cfg.Port == 0 {
				cfg.Port = configfile.DefaultDoltServerPort
				cfg.PortSource = PortSourceExternalHostDefault
			}
		}
	}

	return cfg
}

// IsRunning checks if a managed server is running for this beadsDir.
// Returns a State with Running=true if a valid dolt process is found.
func IsRunning(beadsDir string) (*State, error) {
	data, err := os.ReadFile(pidPath(beadsDir))
	if err != nil {
		if os.IsNotExist(err) {
			return &State{Running: false}, nil
		}
		return nil, fmt.Errorf("reading PID file: %w", err)
	}

	pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
	if err != nil {
		// Corrupt PID file implies stale state; clear the port file too.
		_ = os.Remove(pidPath(beadsDir))
		_ = os.Remove(portPath(beadsDir))
		return &State{Running: false}, nil
	}

	// Check if process is alive
	if !isProcessAlive(pid) {
		// Process is dead — clear all tracked state for this server.
		_ = os.Remove(pidPath(beadsDir))
		_ = os.Remove(portPath(beadsDir))
		return &State{Running: false}, nil
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped OS error for the exact cause
  2. Fix ownership of the .beads directory (avoid running bd with sudo): chown -R $(whoami) .beads
  3. If the PID file is a directory or corrupt, remove it — bd treats it as stale state
  4. Check the mount/filesystem health of the .beads directory

Example fix

// before: running bd as root creates root-owned files in .beads
sudo bd dolt start
// after: run bd as your own user; fix existing ownership
chown -R $(whoami) .beads
bd dolt start
Defensive patterns

Strategy: try-catch

Validate before calling

pidFile := filepath.Join(beadsDir, "daemon.pid")
if fi, err := os.Stat(pidFile); err == nil && fi.IsDir() {
    return fmt.Errorf("%s is a directory; remove it", pidFile)
}

Type guard

func isPIDFileReadErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "reading PID file")
}

Try / catch

state, err := doltserver.IsRunning(beadsDir)
if err != nil {
    if strings.Contains(err.Error(), "permission denied") {
        // repair ownership, then retry
        exec.Command("chown", "-R", os.Getenv("USER"), beadsDir).Run()
        state, err = doltserver.IsRunning(beadsDir)
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Calling IsRunning(beadsDir) (directly or via EnsureRunningDetailed/Start/StopWithForce) when pidPath(beadsDir) exists but cannot be read — permission changed, path is a directory, or an I/O error occurs.

Common situations: The PID file was created by another user (e.g. bd once run under sudo) so the current user can't read it, a directory named like the PID file after a botched restore, or .beads on a failing mount.

Related errors


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