gastownhall/beads · error

cannot remove old dolt database at

Error message

cannot remove old dolt database at %s: %w

Manually delete %s and retry

What it means

Returned by RecoverPreV56DoltDir when os.RemoveAll cannot delete the legacy .dolt/ directory detected as pre-0.56. The recovery path must delete the incompatible embedded-mode database and reinitialize, so a failed removal blocks recovery. The error explicitly tells the user to delete the directory manually and retry.

Solutions

  1. Stop any other bd or dolt processes that may hold files open (pgrep dolt; pgrep bd), then retry the bd command
  2. Delete the directory manually as the error suggests: rm -rf <doltDir>/.dolt, using sudo if ownership differs
  3. Fix ownership: sudo chown -R $(whoami) <doltDir>/.dolt, then re-run bd
  4. Check for read-only mounts or immutable flags (mount | grep ro; chattr -i) if manual deletion also fails

Example fix

// before
# error: cannot remove old dolt database at /repo/.beads/.dolt: remove ...: permission denied
// after
$ sudo rm -rf /repo/.beads/.dolt
$ bd ready   # recovery reinitializes the database
Defensive patterns

Strategy: validation

Validate before calling

dotDolt := filepath.Join(doltDir, ".dolt")
if info, err := os.Stat(dotDolt); err == nil {
    // probe writability before destructive RemoveAll
    probe := filepath.Join(dotDolt, ".bd-probe")
    if f, err := os.OpenFile(probe, os.O_CREATE|os.O_WRONLY, 0600); err != nil {
        return fmt.Errorf("cannot modify %s (ownership/permissions); aborting recovery", dotDolt)
    } else {
        f.Close()
        os.Remove(probe)
    }
}

Try / catch

performed, err := RecoverPreV56DoltDir(doltDir)
if err != nil && strings.Contains(err.Error(), "cannot remove old dolt database") {
    var pathErr *fs.PathError
    if errors.As(err, &pathErr) && errors.Is(pathErr.Err, os.ErrPermission) {
        return fmt.Errorf("stop running bd/dolt processes and remove %s manually (sudo if needed)", pathErr.Path)
    }
    return err
}

Prevention

When it happens

Trigger: RecoverPreV56DoltDir detects .dolt/ without the .bd-dolt-ok marker (pre-0.56 database) during a version upgrade, calls os.RemoveAll(dotDolt), and it fails because a file/directory inside is unwritable, is owned by another user, or is held/open by another process.

Common situations: Pre-0.56 databases created by root or a different user (permission denied on delete); another bd/dolt process still running with open handles; immutable files or a read-only mount; syncing tools (IDE/Dropbox) locking files on some platforms.

Related errors


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

Appendix: source

Thrown at internal/doltserver/doltserver.go:1981

// The data is unrecoverable — the fix is to start fresh.
//
// Returns true if recovery was performed, false if not needed.
func RecoverPreV56DoltDir(doltDir string) (bool, error) {
	dotDolt := filepath.Join(doltDir, ".dolt")
	if _, err := os.Stat(dotDolt); os.IsNotExist(err) {
		return false, nil // No .dolt/ directory — nothing to recover
	}

	markerPath := filepath.Join(doltDir, bdDoltMarker)
	if _, err := os.Stat(markerPath); err == nil {
		return false, nil // Marker exists — database is from 0.56+
	}

	fmt.Fprintf(os.Stderr, "Detected dolt database from an older bd version (pre-0.56).\n")
	fmt.Fprintf(os.Stderr, "Rebuilding dolt database at %s ...\n", doltDir)

	if err := os.RemoveAll(dotDolt); err != nil {
		return false, fmt.Errorf("cannot remove old dolt database at %s: %w\n\n"+
			"Manually delete %s and retry", dotDolt, err, dotDolt)
	}

	// Reinitialize
	if err := ensureDoltInit(doltDir); err != nil {
		return true, fmt.Errorf("recovery: %w", err)
	}

	return true, nil
}

// IsPreV56DoltDir returns true if doltDir contains a .dolt/ directory that
// was NOT created by bd 0.56+ (missing .bd-dolt-ok marker). These databases
// were created by the old embedded Dolt mode and may be incompatible.
// Used by doctor checks to detect potentially problematic dolt databases.
func IsPreV56DoltDir(doltDir string) bool {
	dotDolt := filepath.Join(doltDir, ".dolt")
	if _, err := os.Stat(dotDolt); os.IsNotExist(err) {

View on GitHub (pinned to 71377f2769)