gastownhall/beads · error

failed to create backup directory: %w

Error message

failed to create backup directory: %w

What it means

After resolving the workspace, backupDir creates <beadsDir>/backup with 0700 permissions; if os.MkdirAll fails it wraps the OS error as 'failed to create backup directory: %w'. The underlying cause (permission denied, read-only filesystem, file blocking the path) is preserved for diagnosis.

Source

Thrown at cmd/bd/backup_export.go:51

			gitRepo = filepath.Join(home, gitRepo[2:])
		}
		if _, err := os.Stat(filepath.Join(gitRepo, ".git")); err != nil {
			fmt.Fprintf(os.Stderr, "Warning: backup.git-repo %s is not a git repo, falling back to .beads/backup\n", gitRepo)
		} else {
			dir := filepath.Join(gitRepo, "backup")
			if err := os.MkdirAll(dir, 0700); err != nil {
				return "", fmt.Errorf("failed to create backup dir in git-repo: %w", err)
			}
			return dir, nil
		}
	}
	beadsDir := beads.FindBeadsDir()
	if beadsDir == "" {
		return "", fmt.Errorf("%s; %s", activeWorkspaceNotFoundError(), diagHint())
	}
	dir := filepath.Join(beadsDir, "backup")
	if err := os.MkdirAll(dir, 0700); err != nil {
		return "", fmt.Errorf("failed to create backup directory: %w", err)
	}
	return dir, nil
}

// loadBackupState reads the backup state file, returning a zero state if missing.
func loadBackupState(dir string) (*backupState, error) {
	path := filepath.Join(dir, "backup_state.json")
	data, err := os.ReadFile(path) //nolint:gosec // path is constructed internally
	if os.IsNotExist(err) {
		return &backupState{}, nil
	}
	if err != nil {
		return nil, fmt.Errorf("failed to read backup state: %w", err)
	}
	var state backupState
	if err := json.Unmarshal(data, &state); err != nil {
		return nil, fmt.Errorf("failed to parse backup state: %w", err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Fix ownership/permissions: chown/chmod the .beads directory so the running user can write (mkdir 0700 needs write+execute on .beads)
  2. Remove/rename any non-directory file at .beads/backup blocking creation
  3. Check the wrapped OS error for read-only or no-space conditions and free/repair accordingly
  4. Temporarily configure backup.git-repo to a writable alternate location

Example fix

// before
bd backup export
// error: failed to create backup directory: mkdir .beads/backup: permission denied
// after
sudo chown -R "$USER" .beads && bd backup export
Defensive patterns

Strategy: validation

Validate before calling

beadsDir := beads.FindBeadsDir()
if info, err := os.Stat(beadsDir); err != nil || !info.IsDir() || !writable(beadsDir) {
    return fmt.Errorf(".beads dir missing or not writable: %s", beadsDir)
}

Try / catch

dir, err := backupDir()
if err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrPermission) {
        return fmt.Errorf("fix .beads permissions (chown/chmod) then retry: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Running backup export / auto-backup inside a valid workspace whose .beads directory cannot host a 'backup' subdir: read-only .beads, ownership mismatch (e.g. created by another user/sudo), or a stale file named 'backup' inside .beads.

Common situations: Workspace cloned by root but operated by a normal user, .beads on a read-only mount or synced/locked by another tool, disk-full conditions, or antivirus/backup software holding locks on Windows mounts.

Related errors


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