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

  1. Re-run bd shutdown — if this was a transient race, it will now succeed.
  2. Check permissions on the pidfile: ls -la .beads/*.pid.
  3. If the pidfile is missing but the lock is stuck, wait for the holder to exit or stop it manually, then retry.
  4. 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

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


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