gastownhall/beads · error

probe

Error message

probe %s: %w

What it means

stopAndAcquire failed to even probe the lock file (util.TryLock on proxy.lock or dolt backend lock) with a non-'already locked' error. Since the state of the lock is unknown, Shutdown aborts rather than guessing. The wrapped error is the underlying OS-level failure.

Solutions

  1. Run bd shutdown from the correct workspace root (the one containing .beads).
  2. Check the lock file and directory exist and are writable: ls -la .beads/*.lock.
  3. Inspect the wrapped %w error: ENOENT means wrong rootDir; EACCES means permissions; ENOLCK/EOPNOTSUPP means the filesystem doesn't support locks.
  4. Copy the workspace to a local filesystem if it lives on NFS/network storage.

Example fix

// before
err := proxy.Shutdown("/mnt/nfs/project") // probe ...lock: operation not supported
// after
err := proxy.Shutdown("/home/user/project") // local fs supporting flock
Defensive patterns

Strategy: validation

Validate before calling

fi

Try / catch

if err := proxy.Shutdown(rootDir); err != nil && strings.Contains(err.Error(), "probe ") {
    // inspect wrapped cause: wrong rootDir vs permissions vs unsupported fs
}

Prevention

When it happens

Trigger: util.TryLock returns an error that is neither success nor lockfile.IsLocked — e.g. the lock file's parent directory doesn't exist, permission denied on the lock file, or a filesystem-level flock error.

Common situations: Running shutdown outside the workspace (missing .beads dir), wrong rootDir passed to Shutdown, directory permissions broken after a copy/restore, or a corrupted/exotic filesystem that doesn't support flock (some network mounts).

Related errors


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

Appendix: source

Thrown at internal/storage/dbproxy/proxy/shutdown.go:282

				return nil, fmt.Errorf("kill verified pid %d from %s: %w", pf.Pid, recordPath, killErr)
			}
			if closeErr := handle.Close(); closeErr != nil {
				lock.Unlock()
				return nil, fmt.Errorf("close verified process handle for pid %d: %w", pf.Pid, closeErr)
			}
			waitBudget := max(time.Until(deadline), shutdownPostKillMinimum)
			if waitErr := waitForRecordedProcessExit(pf, waitBudget); waitErr != nil {
				lock.Unlock()
				return nil, fmt.Errorf("confirm verified pid %d stopped: %w", pf.Pid, waitErr)
			}
			if removeErr := pidfile.Remove(rootDir, pidName); removeErr != nil {
				lock.Unlock()
				return nil, fmt.Errorf("remove stopped process record %s: %w", recordPath, removeErr)
			}
			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)

View on GitHub (pinned to 71377f2769)