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
- Check filesystem permissions and free space in the target directory, remove the partial .git directory, and retry the setup
- Verify the created repo is readable (cat .git/HEAD works) and no security software is locking .git
- 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
- Treat head_tree_id_or_empty() as fallible: propagate with ? like the other 12 call sites in the repo
- Check disk writability before running interactive repo initialization
- Keep antivirus/backup exclusions for the repo directory in locked-down environments
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
- BUG: value must be valid ref name
- BUG: we have to avoid using these legacy types
- known statically
- no illformed UTF8
- valid commit that can be parsed: TODO - allow it to return…
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)