gastownhall/beads · error
read held-lock record %s: %w
Error message
read held-lock record %s: %w
What it means
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.
Source
Thrown at internal/storage/dbproxy/proxy/shutdown.go:296
}
return lock, nil
case !lockfile.IsLocked(err):
return nil, fmt.Errorf("probe %s: %w", lockPath, err)
}
pf, readErr := pidfile.Read(rootDir, pidName)
if readErr != nil {
if isMalformedPIDFileError(readErr) {
return nil, unverifiableProcessError(
"shutdown",
recordPath,
0,
readErr,
unverifiableProcessChecks{},
)
}
return nil, fmt.Errorf("read held-lock record %s: %w", recordPath, readErr)
}
if pf != nil {
if validateErr := validateKillRecord(rootDir, pf, wantKind, checks); validateErr != nil {
dead, live, probeErr := classifyInvalidKillRecord(pf, validateErr)
if !dead {
if probeErr != nil {
validateErr = errors.Join(validateErr, fmt.Errorf("probe recorded pid: %w", probeErr))
}
return nil, unverifiableProcessError(
"shutdown",
recordPath,
pf.Pid,
validateErr,
unverifiableProcessChecks{
LiveEstablished: live,
LegacyProxy: live &&
wantKind == pidfile.KindProxy &&
errors.Is(validateErr, pidfile.ErrLegacySchema),View on GitHub (pinned to 71377f2769)
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).
Example fix
// before $ bd dolt stop // read held-lock record .beads/dolt.pid: permission denied // after $ ls -la .beads/*.pid; chmod u+rw .beads/*.pid $ bd dolt stop
Defensive patterns
Strategy: retry
Validate before calling
[ -r .beads/dolt.pid -a -w .beads ] && echo ok || echo 'pidfile unreadable or dir unwritable'
Try / catch
if err := proxy.Shutdown(rootDir); err != nil && strings.Contains(err.Error(), "read held-lock record") {
time.Sleep(500 * time.Millisecond)
err = proxy.Shutdown(rootDir) // transient race usually resolves
} Prevention
- Don't concurrently delete or rewrite .beads control files.
- Keep pidfile permissions intact.
- Retry once before manual intervention — mid-flight races are transient.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- errStartInterrupted
- discover spawned proxy: %w
- timeout (%s) waiting for spawn marker %s; wait for the in-pr
- probe %s: %w
- timeout (%s) acquiring %s after inspecting pid %d at %s; sto
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/526700ffcc6c68c1.
Report an issue: GitHub.