gastownhall/beads · error
timeout (%s) acquiring %s after inspecting pid %d at %s; sto
Error message
timeout (%s) acquiring %s after inspecting pid %d at %s; stop the lock owner with the binary that started it, then retry
What it means
stopAndAcquire spent its whole 5-second budget (shutdownConfirmDeadline) polling without acquiring the lock and without confirming the recorded process stopped. Shutdown gives up with this actionable message telling the user to stop the lock owner with the binary that started it and retry. pid 0 in the message means no usable pidfile record was available to identify the holder.
Source
Thrown at internal/storage/dbproxy/proxy/shutdown.go:351
if !dead {
if killErr := handle.Kill(); killErr != nil {
_ = handle.Close()
return nil, fmt.Errorf("kill verified pid %d from %s: %w", pf.Pid, recordPath, killErr)
}
if closeErr := handle.Close(); closeErr != nil {
return nil, fmt.Errorf("close verified process handle for pid %d: %w", pf.Pid, closeErr)
}
stopped = pf
}
}
}
if time.Now().After(deadline) {
pid := 0
if pf != nil {
pid = pf.Pid
}
return nil, fmt.Errorf(
"timeout (%s) acquiring %s after inspecting pid %d at %s; stop the lock owner with the binary that started it, then retry",
shutdownConfirmDeadline,
lockPath,
pid,
recordPath,
)
}
time.Sleep(shutdownConfirmPoll)
}
}
func validateKillRecord(
rootDir string,
pf *pidfile.PidFile,
wantKind string,
checks killRecordChecks,
) error {
if err := pf.ValidateV2(wantKind); err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Identify the lock holder (fuser -v <lockPath> or lsof <lockPath>) and stop it with the matching bd binary, then retry shutdown.
- Check the recorded pid in the message; if nonzero, inspect it with ps -p <pid> to see what it is.
- Wait for the running operation to finish (e.g. a long bd command) and retry.
- If no process holds the lock (pid 0, no lsof output), remove the stale lock file and retry.
Example fix
// before $ bd dolt stop // timeout (5s) acquiring .beads/proxy.lock after inspecting pid 0 ... // after $ lsof .beads/proxy.lock # find holder $ bd dolt stop # retry once holder exits # or, if truly stale: $ rm -f .beads/proxy.lock && bd dolt stop
Defensive patterns
Strategy: fallback
Validate before calling
lsof .beads/proxy.lock .beads/dolt-backend.lock 2>/dev/null || echo 'no live lock holders; locks are stale'
Try / catch
if err := proxy.Shutdown(rootDir); err != nil && strings.Contains(err.Error(), "timeout (") {
// fallback: stop the holder out-of-band, or purge stale control files
// proxy.PurgeControlFiles(rootDir) — only after confirming no live holder
} Prevention
- Ensure all long bd operations finish before shutting down.
- Run shutdown from the same binary/version that started the daemon.
- Before purging control files, verify with lsof/fuser that no process holds the lock.
When it happens
Trigger: The lock (proxy.lock or backend lock) stayed continuously held for >5s during proxy.Shutdown — a healthy long-running daemon that isn't exiting, a hung lock holder, or a loop of competing bd processes reacquiring the lock.
Common situations: Another bd daemon is legitimately still running and busy; a crashed process left the lock held while its pidfile was removed (holder unidentifiable, pid 0); an interactive bd session or long-running command holding the lock; NFS where flock semantics misbehave.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- timeout clearing spawn marker %s
- timeout (%s) waiting for spawn marker %s; wait for the in-pr
- errStartInterrupted
- procid: process %d still matches token after fatal signal an
- timeout waiting for cache lock on %s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/b4280c5b82c5d751.
Report an issue: GitHub.