{"record":{"id":"526700ffcc6c68c1","repo":"gastownhall/beads","slug":"read-held-lock-record-s-w","errorCode":null,"errorMessage":"read held-lock record %s: %w","messagePattern":"read held-lock record (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/shutdown.go","lineNumber":296,"sourceCode":"\t\t\t}\n\t\t\treturn lock, nil\n\n\t\tcase !lockfile.IsLocked(err):\n\t\t\treturn nil, fmt.Errorf(\"probe %s: %w\", lockPath, err)\n\t\t}\n\n\t\tpf, readErr := pidfile.Read(rootDir, pidName)\n\t\tif readErr != nil {\n\t\t\tif isMalformedPIDFileError(readErr) {\n\t\t\t\treturn nil, unverifiableProcessError(\n\t\t\t\t\t\"shutdown\",\n\t\t\t\t\trecordPath,\n\t\t\t\t\t0,\n\t\t\t\t\treadErr,\n\t\t\t\t\tunverifiableProcessChecks{},\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"read held-lock record %s: %w\", recordPath, readErr)\n\t\t}\n\t\tif pf != nil {\n\t\t\tif validateErr := validateKillRecord(rootDir, pf, wantKind, checks); validateErr != nil {\n\t\t\t\tdead, live, probeErr := classifyInvalidKillRecord(pf, validateErr)\n\t\t\t\tif !dead {\n\t\t\t\t\tif probeErr != nil {\n\t\t\t\t\t\tvalidateErr = errors.Join(validateErr, fmt.Errorf(\"probe recorded pid: %w\", probeErr))\n\t\t\t\t\t}\n\t\t\t\t\treturn nil, unverifiableProcessError(\n\t\t\t\t\t\t\"shutdown\",\n\t\t\t\t\t\trecordPath,\n\t\t\t\t\t\tpf.Pid,\n\t\t\t\t\t\tvalidateErr,\n\t\t\t\t\t\tunverifiableProcessChecks{\n\t\t\t\t\t\t\tLiveEstablished: live,\n\t\t\t\t\t\t\tLegacyProxy: live &&\n\t\t\t\t\t\t\t\twantKind == pidfile.KindProxy &&\n\t\t\t\t\t\t\t\terrors.Is(validateErr, pidfile.ErrLegacySchema),","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/shutdown.go#L278-L314","documentation":"The lock is currently held by another live process, so stopAndAcquire reads the pidfile to identify the owner; reading that record failed with a non-malformed error. Shutdown aborts because it cannot determine who holds the lock, and it will not kill blindly.","triggerScenarios":"During proxy.Shutdown, the lock is contended and pidfile.Read returns a real I/O error (permission denied, deleted mid-read, not a malformed-content error which takes a different path).","commonSituations":"The pidfile was removed or chmod-ed by another process between lock contention and the read; permissions on .beads were altered; the pidfile is on a flaky network mount.","solutions":["Re-run bd shutdown — if this was a transient race, it will now succeed.","Check permissions on the pidfile: ls -la .beads/*.pid.","If the pidfile is missing but the lock is stuck, wait for the holder to exit or stop it manually, then retry.","Verify the workspace root is correct and local (not on NFS)."],"exampleFix":"// before\n$ bd dolt stop // read held-lock record .beads/dolt.pid: permission denied\n// after\n$ ls -la .beads/*.pid; chmod u+rw .beads/*.pid\n$ bd dolt stop","handlingStrategy":"retry","validationCode":"[ -r .beads/dolt.pid -a -w .beads ] && echo ok || echo 'pidfile unreadable or dir unwritable'","typeGuard":null,"tryCatchPattern":"if err := proxy.Shutdown(rootDir); err != nil && strings.Contains(err.Error(), \"read held-lock record\") {\n    time.Sleep(500 * time.Millisecond)\n    err = proxy.Shutdown(rootDir) // transient race usually resolves\n}","preventionTips":["Don't concurrently delete or rewrite .beads control files.","Keep pidfile permissions intact.","Retry once before manual intervention — mid-flight races are transient."],"tags":["pidfile","lockfile","shutdown","race"],"backgroundTag":"pidfile-read-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}