gastownhall/beads · error

failed to create backup dir in git-repo: %w

Error message

failed to create backup dir in git-repo: %w

What it means

backupDir determines the local backup directory. When backup.git-repo is configured and points at a valid git repo, it creates <gitRepo>/backup with 0700 permissions; if os.MkdirAll fails (permissions, read-only filesystem, path conflicts), it wraps the OS error as 'failed to create backup dir in git-repo: %w'. The wrapped cause names the exact filesystem problem.

Source

Thrown at cmd/bd/backup_export.go:40

}

// backupDir returns the backup directory path, creating it if needed.
// When backup.git-repo is set to a valid git repo, returns a backup/ subdirectory
// inside that repo. Otherwise it requires an active beads workspace and uses its
// backup/ subdirectory.
func backupDir() (string, error) {
	gitRepo := config.GetString("backup.git-repo")
	if gitRepo != "" {
		if strings.HasPrefix(gitRepo, "~/") {
			home, _ := os.UserHomeDir()
			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")

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check permissions on the git-repo parent directory and ensure the process user can create directories there
  2. Remove or rename any non-directory file at <gitRepo>/backup that blocks MkdirAll
  3. Unset backup.git-repo to fall back to .beads/backup inside the writable workspace
  4. Check the wrapped OS error (permission denied vs read-only) and fix the mount or ACL accordingly

Example fix

// before
# backup.git-repo = /mnt/readonly/repo
bd backup export
// error: failed to create backup dir in git-repo: mkdir /mnt/readonly/repo/backup: read-only file system
// after: point git-repo at a writable location or drop the setting
bd config set backup.git-repo /home/user/repo  # or unset it
Defensive patterns

Strategy: validation

Validate before calling

gitRepo := cfg.BackupGitRepo
if st, err := os.Stat(filepath.Join(gitRepo, ".git")); err == nil && st.IsDir() {
    if f, err := os.OpenFile(gitRepo, os.O_WRONLY, 0700); err != nil {
        return fmt.Errorf("git-repo %s not writable: %w", gitRepo, err)
    } else { f.Close() }
}

Try / catch

dir, err := backupDir()
if err != nil {
    if strings.Contains(err.Error(), "failed to create backup dir in git-repo") {
        // fall back to default .beads/backup or abort with a clear message
    }
    return err
}

Prevention

When it happens

Trigger: Running backup export (runBackupExport) or auto-backup (maybeAutoBackup) with backup.git-repo set, where creating <gitRepo>/backup fails: parent dir not writable, gitRepo path on a read-only mount, or a file named 'backup' already exists at that path.

Common situations: git-repo on a read-only CI volume, permission-restricted shared server, SELinux/AppArmor restrictions, or a stale regular file blocking the 'backup' directory path.

Related errors


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