{"record":{"id":"737de67aa2be797f","repo":"gastownhall/beads","slug":"pidfile-legacy-schema","errorCode":null,"errorMessage":"pidfile: legacy schema","messagePattern":"pidfile: legacy schema","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/pidfile/pidfile.go","lineNumber":32,"sourceCode":"\tPid         int    `json:\"pid\"`\n\tPort        int    `json:\"port\"`\n\tUpstreamID  string `json:\"upstream_id,omitempty\"`\n\tSchema      int    `json:\"schema,omitempty\"`\n\tKind        string `json:\"kind,omitempty\"`\n\tBirth       string `json:\"birth,omitempty\"`\n\tRootID      string `json:\"root_id,omitempty\"`\n\tControlPort int    `json:\"control_port,omitempty\"`\n}\n\nconst SchemaV2 = 2\n\nconst (\n\tKindProxy       = \"db-proxy\"\n\tKindDoltBackend = \"dolt-backend\"\n)\n\nvar (\n\tErrLegacySchema = errors.New(\"pidfile: legacy schema\")\n\tErrBadPid       = errors.New(\"pidfile: invalid pid\")\n\tErrBadPort      = errors.New(\"pidfile: invalid port\")\n\tErrKindMismatch = errors.New(\"pidfile: kind mismatch\")\n\tErrMissingBirth = errors.New(\"pidfile: missing birth token\")\n)\n\n// ValidateV2 validates the fields required for a schema v2 pidfile.\nfunc (p *PidFile) ValidateV2(wantKind string) error {\n\tif p.Schema < SchemaV2 {\n\t\treturn ErrLegacySchema\n\t}\n\tif p.Pid <= 0 {\n\t\treturn ErrBadPid\n\t}\n\tif p.Port < 1 || p.Port > 65535 || (p.ControlPort != 0 && (p.ControlPort < 1 || p.ControlPort > 65535)) {\n\t\treturn ErrBadPort\n\t}\n\tif p.Kind != wantKind {","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/pidfile/pidfile.go#L14-L50","documentation":"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.","triggerScenarios":"Calling ValidateV2(wantKind) on a PidFile whose Schema < SchemaV2; readAndDial or stopAndAcquire reading a pidfile written by an older beads/dbproxy binary.","commonSituations":"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.","solutions":["Delete the stale pidfile and restart the proxy so a fresh schema-v2 pidfile is written","Upgrade all beads binaries sharing the workspace to the same (current) version","Use errors.Is(err, pidfile.ErrLegacySchema) to detect the case and fall back to legacy cleanup/stop logic before re-acquiring"],"exampleFix":"// before\nif err := pf.ValidateV2(pidfile.KindProxy); err != nil { return err }\n// after\nif err := pf.ValidateV2(pidfile.KindProxy); err != nil {\n    if errors.Is(err, pidfile.ErrLegacySchema) {\n        _ = os.Remove(pidfilePath) // drop v1 pidfile, re-acquire fresh\n        return acquireProxy(rootDir)\n    }\n    return err\n}","handlingStrategy":"type-guard","validationCode":"data, _ := os.ReadFile(pidfilePath)\nvar pf pidfile.PidFile\njson.Unmarshal(data, &pf)\nif pf.Schema < pidfile.SchemaV2 { /* regenerate pidfile first */ }","typeGuard":"func isLegacySchema(err error) bool { return errors.Is(err, pidfile.ErrLegacySchema) }","tryCatchPattern":"if err := pf.ValidateV2(pidfile.KindProxy); err != nil {\n    if errors.Is(err, pidfile.ErrLegacySchema) { /* regenerate */ }\n    return err\n}","preventionTips":["Keep all beads binaries in a workspace at the same version","Clean up pidfiles when upgrading","Never hand-edit pidfiles"],"tags":["pidfile","schema","upgrade","storage"],"backgroundTag":"legacy-schema-pidfile","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}