gitbutlerapp/gitbutler · error

repo access failed

Error message

repo access failed

What it means

create_empty_initial_commit() runs during interactive `but` setup after the user answers 'y' to initializing a new repository (setup.rs:612-651). repo.head_tree_id_or_empty() returns a Result: on an unborn HEAD it yields the well-known empty tree id, but it errors when the repository cannot be accessed (unreadable .git/HEAD, object-store failure). The expect("repo access failed") turns that gix::Error into a panic, even though every other call site in the workspace propagates it with `?` (e.g. but-workspace/src/worktree.rs:22).

Solutions

  1. Check filesystem permissions and free space in the target directory, remove the partial .git directory, and retry the setup
  2. Verify the created repo is readable (cat .git/HEAD works) and no security software is locking .git
  3. As a contributor: replace the expect with error propagation (see exampleFix) since the identical call is fallible everywhere else in the codebase

Example fix

// before
let empty_tree = repo.head_tree_id_or_empty().expect("repo access failed");

// after
let empty_tree = repo
    .head_tree_id_or_empty()
    .context("failed to read HEAD tree of the freshly initialized repository")?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Fail fast before setup: prove the target dir can hold a readable file
let probe = current_dir.join(".git-probe");
std::fs::write(&probe, b"x")?;
std::fs::read_to_string(&probe)?;
std::fs::remove_file(&probe)?;

Try / catch

// Propagate instead of panicking - same call is fallible everywhere else
let empty_tree = repo
    .head_tree_id_or_empty()
    .context("failed to read HEAD tree of the freshly initialized repository")?;

Prevention

When it happens

Trigger: Running the `but` setup flow in a directory with no git repository and confirming initialization, when reading the freshly created .git/HEAD fails: disk I/O error, permission denial, or antivirus/file-lock interference right after gix::init().

Common situations: Read-only or full filesystems, containers with restricted mounts, security software locking .git files immediately after creation, or partial gix::init() failures leaving a broken .git directory.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/1967abf3ff11980b. Report an issue: GitHub.

Appendix: source

Thrown at crates/but/src/command/legacy/setup.rs:656

            create_empty_initial_commit(&repo)?;

            writeln!(
                &mut progress as &mut dyn FmtWrite,
                "{}",
                t.success
                    .paint("Initialized a new repository and created an empty first commit.\n")
            )?;
            return Ok(repo);
        }
    }

    Err(anyhow::anyhow!("No git repository found."))
}

fn create_empty_initial_commit(repo: &gix::Repository) -> anyhow::Result<()> {
    // In an unborn repo, this returns the well-known empty-tree id.
    // (It works even if the empty tree object isn’t physically in the ODB.)
    let empty_tree = repo.head_tree_id_or_empty().expect("repo access failed"); // -> Id<'_>
    let empty_tree = empty_tree.detach(); // -> ObjectId (optional; commit() accepts Into<ObjectId> anyway)

    // No parents for the first commit. Update HEAD (writes through to refs/heads/main).
    repo.commit(
        "HEAD",
        "Initial empty commit\n",
        empty_tree,
        std::iter::empty::<gix::hash::ObjectId>(),
    )?;

    Ok(())
}

View on GitHub (pinned to caf1f223d3)