gastownhall/beads · error

read backend record

Error message

read backend record %s: %w

What it means

cleanupOrphanBackend reads the Dolt backend PID record (pidfile) at <rootDir>/<PIDFileName> to decide whether a stale backend process must be killed. This error wraps any read failure that is NOT a malformed-record error (malformed records are routed to unverifiableProcessError instead). It signals the cleanup pass could not even inspect the record, so orphan cleanup was aborted.

Solutions

  1. Check permissions and ownership of the record file shown in the message and fix them (chown/chmod) so the current user can read it.
  2. Verify the file is a valid pidfile; if it is corrupt or stale, remove or rename it (e.g. to <record>.stale-<timestamp>) and retry.
  3. Re-run the command under the same user that created the workspace; avoid mixing sudo and non-sudo bd invocations.
  4. If the underlying OS error persists, investigate the filesystem (disk full, mount issues) reported by the wrapped cause.

Example fix

// before (record unreadable)
$ bd doctor
Error: read backend record /ws/.beads/dbproxy/dolt-backend.pid: open ...: permission denied
// after
$ sudo chown $(whoami) /ws/.beads/dbproxy/dolt-backend.pid
$ bd doctor  # cleanup proceeds
Defensive patterns

Strategy: validation

Validate before calling

recordPath := filepath.Join(rootDir, "dolt-backend.pid")
if _, err := os.Stat(recordPath); err != nil {
    if errors.Is(err, fs.ErrNotExist) {
        return nil // nothing to clean
    }
    return fmt.Errorf("record %s not readable before cleanup: %w", recordPath, err)
}

Type guard

func isMalformed(err error) bool {
    var target *fs.PathError
    return errors.As(err, &target) && errors.Is(target.Err, os.ErrPermission)
}

Try / catch

if err := cleanupOrphanBackend(rootDir); err != nil {
    var pathErr *fs.PathError
    if errors.As(err, &pathErr) && errors.Is(pathErr.Err, os.ErrPermission) {
        // surface guidance: fix ownership/permissions of pathErr.Path
    }
    return err
}

Prevention

When it happens

Trigger: pidfile.Read returns an error other than a malformed-PID-file error: the record file exists but is unreadable (permission denied, I/O error), or an unexpected filesystem error occurs while opening/decoding it at internal/storage/dbproxy/proxy/endpoint.go:749,779.

Common situations: File permissions changed on the workspace .beads/dbproxy directory (e.g. record written by a different user or root); disk/IO errors; a partially-written record whose decode fails in a way not classified as malformed; running bd from a different account after a sudo invocation.

Related errors


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

Appendix: source

Thrown at internal/storage/dbproxy/proxy/endpoint.go:779

		return fmt.Errorf(
			"refusing backend cleanup: %s is held while proxy lock is free (recorded pid %d at %s); stop the process holding the child lock or remove the stale lock owner before retrying",
			childLockPath, pid, recordPath,
		)
	default:
		return fmt.Errorf("probe backend lock %s: %w", childLockPath, lockErr)
	}

	if readErr != nil {
		if isMalformedPIDFileError(readErr) {
			return unverifiableProcessError(
				"backend cleanup",
				recordPath,
				0,
				readErr,
				unverifiableProcessChecks{},
			)
		}
		return fmt.Errorf("read backend record %s: %w", recordPath, readErr)
	}
	if pf == nil {
		return nil
	}
	if err := pf.ValidateV2(pidfile.KindDoltBackend); err != nil {
		dead, live, probeErr := probeUnverifiablePID(pf.Pid)
		if dead {
			if _, quarantineErr := quarantineRecord(rootDir, server.PIDFileName, time.Now()); quarantineErr != nil {
				return fmt.Errorf("quarantine dead unverifiable backend record: %w", quarantineErr)
			}
			return nil
		}
		if probeErr != nil {
			err = errors.Join(err, fmt.Errorf("probe recorded pid: %w", probeErr))
		}
		return unverifiableProcessError(
			"backend cleanup",
			recordPath,

View on GitHub (pinned to 71377f2769)