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

  1. Identify the lock holder (fuser -v <lockPath> or lsof <lockPath>) and stop it with the matching bd binary, then retry shutdown.
  2. Check the recorded pid in the message; if nonzero, inspect it with ps -p <pid> to see what it is.
  3. Wait for the running operation to finish (e.g. a long bd command) and retry.
  4. 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

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

Related errors


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