gastownhall/beads · error

pidfile: legacy schema

Error message

pidfile: legacy schema

What it means

ErrLegacySchema indicates a pidfile on disk uses schema v1 (or older) while the current dbproxy code requires schema v2. It is returned by PidFile.ValidateV2 (and readAndDial/stopAndAcquire which call it) when the parsed pidfile's Schema field is below SchemaV2, so the library refuses to trust legacy pidfiles that lack v2 fields like birth tokens.

Source

Thrown at internal/storage/dbproxy/pidfile/pidfile.go:32

	Pid         int    `json:"pid"`
	Port        int    `json:"port"`
	UpstreamID  string `json:"upstream_id,omitempty"`
	Schema      int    `json:"schema,omitempty"`
	Kind        string `json:"kind,omitempty"`
	Birth       string `json:"birth,omitempty"`
	RootID      string `json:"root_id,omitempty"`
	ControlPort int    `json:"control_port,omitempty"`
}

const SchemaV2 = 2

const (
	KindProxy       = "db-proxy"
	KindDoltBackend = "dolt-backend"
)

var (
	ErrLegacySchema = errors.New("pidfile: legacy schema")
	ErrBadPid       = errors.New("pidfile: invalid pid")
	ErrBadPort      = errors.New("pidfile: invalid port")
	ErrKindMismatch = errors.New("pidfile: kind mismatch")
	ErrMissingBirth = errors.New("pidfile: missing birth token")
)

// ValidateV2 validates the fields required for a schema v2 pidfile.
func (p *PidFile) ValidateV2(wantKind string) error {
	if p.Schema < SchemaV2 {
		return ErrLegacySchema
	}
	if p.Pid <= 0 {
		return ErrBadPid
	}
	if p.Port < 1 || p.Port > 65535 || (p.ControlPort != 0 && (p.ControlPort < 1 || p.ControlPort > 65535)) {
		return ErrBadPort
	}
	if p.Kind != wantKind {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Delete the stale pidfile and restart the proxy so a fresh schema-v2 pidfile is written
  2. Upgrade all beads binaries sharing the workspace to the same (current) version
  3. Use errors.Is(err, pidfile.ErrLegacySchema) to detect the case and fall back to legacy cleanup/stop logic before re-acquiring

Example fix

// before
if err := pf.ValidateV2(pidfile.KindProxy); err != nil { return err }
// after
if err := pf.ValidateV2(pidfile.KindProxy); err != nil {
    if errors.Is(err, pidfile.ErrLegacySchema) {
        _ = os.Remove(pidfilePath) // drop v1 pidfile, re-acquire fresh
        return acquireProxy(rootDir)
    }
    return err
}
Defensive patterns

Strategy: type-guard

Validate before calling

data, _ := os.ReadFile(pidfilePath)
var pf pidfile.PidFile
json.Unmarshal(data, &pf)
if pf.Schema < pidfile.SchemaV2 { /* regenerate pidfile first */ }

Type guard

func isLegacySchema(err error) bool { return errors.Is(err, pidfile.ErrLegacySchema) }

Try / catch

if err := pf.ValidateV2(pidfile.KindProxy); err != nil {
    if errors.Is(err, pidfile.ErrLegacySchema) { /* regenerate */ }
    return err
}

Prevention

When it happens

Trigger: Calling ValidateV2(wantKind) on a PidFile whose Schema < SchemaV2; readAndDial or stopAndAcquire reading a pidfile written by an older beads/dbproxy binary.

Common situations: Upgrading the beads binary while a stale proxy pidfile from the previous version remains on disk; mixed-version clusters where an old binary wrote the pidfile; restored workspaces from backups made by older versions.

Related errors


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