gitbutlerapp/gitbutler · error

Repository installers require the main worktree

Error message

Repository installers require the main worktree

What it means

The `but-debug dump repo-installer` command reconstructs an installer archive for a repository and requires the main worktree. After ensuring a non-bare repo, it asserts repo.git_dir() == repo.common_dir() — i.e. the opened repo is not a linked worktree — and fails with this message otherwise.

Solutions

  1. Run the command from the main worktree of the repository (the one containing the real .git directory).
  2. cd to `git worktree list`'s first (main) entry and re-run.
  3. If only a linked worktree is available, clone/open the primary checkout first.

Example fix

// before
$ cd ~/work/myrepo-feature-wt && but-debug dump repo-installer
// after
$ cd ~/work/myrepo && but-debug dump repo-installer   # main worktree
Defensive patterns

Strategy: validation

Validate before calling

main_wt=$(git worktree list --porcelain | head -1 | cut -d' ' -f2)
cd "$main_wt" && but-debug dump repo-installer

Type guard

fn is_main_worktree(repo: &gix::Repository) -> bool { repo.git_dir() == repo.common_dir() }

Prevention

When it happens

Trigger: Running `but-debug dump repo-installer` from inside a git linked worktree (created via `git worktree add`), where git_dir() is .git/worktrees/<name> and differs from common_dir().

Common situations: Developers debugging from a secondary worktree checkout; CI that checks out via `git worktree add`; monorepo workflows that keep multiple worktrees.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/50cfdead2e8c2ab8. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-debug/src/command/dump/repo_installer.rs:39

/// Execute the `dump repo-installer` subcommand.
pub(super) fn run(
    args: &Args,
    installer_args: &RepoInstallerArgs,
    out: &mut dyn io::Write,
    err: &mut dyn io::Write,
) -> Result<()> {
    let current_dir = effective_current_dir(args)?;
    let repo = setup::repo_from_args(args).with_context(|| {
        format!(
            "Could not discover Git repository at '{}'",
            current_dir.display()
        )
    })?;
    let repo = gix::open_opts(repo.git_dir().canonicalize()?, repo.open_options().clone())?;
    let workdir = repo
        .workdir()
        .context("Repository installers require a non-bare repository")?;
    ensure!(
        repo.git_dir() == repo.common_dir(),
        "Repository installers require the main worktree"
    );

    let project_data_dir = repo
        .gitbutler_storage_path()
        .context("Could not locate GitButler project data")?;
    ensure!(
        project_data_dir.is_dir(),
        "GitButler project data does not exist at '{}'",
        project_data_dir.display()
    );

    let base = archive_base_name(&repo)?;
    let archive_root = format!("{base}-repo-installer");
    let output_path = match &installer_args.archive.output {
        Some(path) => current_dir.join(path),
        None => default_output_path(&repo, "repo-installer")?,

View on GitHub (pinned to 58e5313667)