gastownhall/beads · error

backup destination is not a directory: %s

Error message

backup destination is not a directory: %s

What it means

BackupDatabase validated its destination with os.Stat and found the path exists but is not a directory (e.g. it is a regular file, symlink to a file, or device). A file:// Dolt backup remote requires a directory target, so the backup is refused before any sync. Note this message embeds the path, not a wrapped cause.

Source

Thrown at internal/storage/dolt/store.go:1297

	}
	defer db.Close()
	return versioncontrolops.BackupSync(ctx, db, name)
}

// BackupRemove removes a configured Dolt backup destination.
func (s *DoltStore) BackupRemove(ctx context.Context, name string) error {
	return versioncontrolops.BackupRemove(ctx, s.db, name)
}

// BackupDatabase registers dir as a file:// Dolt backup remote and syncs
// the full database to it, preserving complete commit history.
func (s *DoltStore) BackupDatabase(ctx context.Context, dir string) error {
	info, err := os.Stat(dir)
	if err != nil {
		return fmt.Errorf("backup destination does not exist: %w", err)
	}
	if !info.IsDir() {
		return fmt.Errorf("backup destination is not a directory: %s", dir)
	}

	backupURL, err := versioncontrolops.DirToFileURL(dir)
	if err != nil {
		return err
	}
	backupName := "backup_export"

	syncDB, err := s.oneShotConn(0)
	if err != nil {
		return err
	}
	defer syncDB.Close()

	// Register as a backup remote (idempotent — remove first if exists).
	_ = versioncontrolops.BackupRemove(ctx, s.db, backupName)
	if err := versioncontrolops.BackupAdd(ctx, s.db, backupName, backupURL); err != nil {
		// Another backup (e.g. "default" registered by `bd backup init`) may

View on GitHub (pinned to 71377f2769)

Solutions

  1. Move or remove the file at that path (rm/mv), then create a directory: mkdir -p <dir>.
  2. Choose a different destination directory that is empty or dedicated to backups.
  3. If the path is a symlink, confirm it resolves to a directory; if it resolves to a file, fix the link target.
  4. Check your script/config for a variable that accidentally holds a filename instead of a directory path.

Example fix

// before: path holds an old backup.tar
store.BackupDatabase(ctx, "/backups/beads") // not a directory
// after
os.Rename("/backups/beads", "/backups/beads.tar.bak")
os.MkdirAll("/backups/beads", 0o755)
store.BackupDatabase(ctx, "/backups/beads")
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(dir)
if err == nil && !info.IsDir() {
    return fmt.Errorf("%s exists but is not a directory; choose another path", dir)
}

Type guard

func isExistingDir(path string) bool {
    info, err := os.Stat(path)
    return err == nil && info.IsDir()
}

Try / catch

if err := store.BackupDatabase(ctx, dir); err != nil {
    if strings.Contains(err.Error(), "is not a directory") {
        os.Rename(dir, dir+".file.bak")
        os.MkdirAll(dir, 0o755)
        err = store.BackupDatabase(ctx, dir)
    }
}

Prevention

When it happens

Trigger: Calling DoltStore.BackupDatabase(ctx, dir) where dir exists but info.IsDir() is false — a plain file occupies the path, or the path is a socket/device node.

Common situations: A previous single-file backup or tarball sits at the path the user now wants to use as a backup directory; a script wrote a marker file to the intended backup location; tab-completion picked a similarly named file.

Related errors


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