{"record":{"id":"b4280c5b82c5d751","repo":"gastownhall/beads","slug":"timeout-s-acquiring-s-after-inspecting-pid-d","errorCode":null,"errorMessage":"timeout (%s) acquiring %s after inspecting pid %d at %s; stop the lock owner with the binary that started it, then retry","messagePattern":"timeout \\((.+?)\\) acquiring (.+?) after inspecting pid (.+?) at (.+?); stop the lock owner with the binary that started it, then retry","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/shutdown.go","lineNumber":351,"sourceCode":"\t\t\t\tif !dead {\n\t\t\t\t\tif killErr := handle.Kill(); killErr != nil {\n\t\t\t\t\t\t_ = handle.Close()\n\t\t\t\t\t\treturn nil, fmt.Errorf(\"kill verified pid %d from %s: %w\", pf.Pid, recordPath, killErr)\n\t\t\t\t\t}\n\t\t\t\t\tif closeErr := handle.Close(); closeErr != nil {\n\t\t\t\t\t\treturn nil, fmt.Errorf(\"close verified process handle for pid %d: %w\", pf.Pid, closeErr)\n\t\t\t\t\t}\n\t\t\t\t\tstopped = pf\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif time.Now().After(deadline) {\n\t\t\tpid := 0\n\t\t\tif pf != nil {\n\t\t\t\tpid = pf.Pid\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\"timeout (%s) acquiring %s after inspecting pid %d at %s; stop the lock owner with the binary that started it, then retry\",\n\t\t\t\tshutdownConfirmDeadline,\n\t\t\t\tlockPath,\n\t\t\t\tpid,\n\t\t\t\trecordPath,\n\t\t\t)\n\t\t}\n\t\ttime.Sleep(shutdownConfirmPoll)\n\t}\n}\n\nfunc validateKillRecord(\n\trootDir string,\n\tpf *pidfile.PidFile,\n\twantKind string,\n\tchecks killRecordChecks,\n) error {\n\tif err := pf.ValidateV2(wantKind); err != nil {","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/shutdown.go#L333-L369","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n$ bd dolt stop // timeout (5s) acquiring .beads/proxy.lock after inspecting pid 0 ...\n// after\n$ lsof .beads/proxy.lock        # find holder\n$ bd dolt stop                  # retry once holder exits\n# or, if truly stale:\n$ rm -f .beads/proxy.lock && bd dolt stop","handlingStrategy":"fallback","validationCode":"lsof .beads/proxy.lock .beads/dolt-backend.lock 2>/dev/null || echo 'no live lock holders; locks are stale'","typeGuard":null,"tryCatchPattern":"if err := proxy.Shutdown(rootDir); err != nil && strings.Contains(err.Error(), \"timeout (\") {\n    // fallback: stop the holder out-of-band, or purge stale control files\n    // proxy.PurgeControlFiles(rootDir) — only after confirming no live holder\n}","preventionTips":["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."],"tags":["timeout","lockfile","shutdown","concurrency"],"backgroundTag":"lock-acquire-timeout","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}