helix-editor/helix · warning · anyhow::Error
working tree not found
Error message
working tree not found
What it means
helix's git diff provider (helix-vcs/src/git.rs) emulates `git status` via gix and requires a worktree: status() calls repo.workdir() and errors with 'working tree not found' when it returns None. gix returns None for bare repositories — repos without a checkout, e.g. created with `git init --bare`, server-side mirrors (*.git), or when helix is opened directly inside a .git directory. The error is delivered to the changed-files callback, so VCS features (changed-file picker, diff gutter) are simply unavailable there.
Source
Thrown at helix-vcs/src/git.rs:151
let (repo_path, _trust_from_ownership) = gix::discover::upwards_opts(path, discover_options)
.context("failed to discover git repo")?;
let (git_dir, _work_dir) = repo_path.into_repository_and_work_tree_directories();
let options = gix::open::Options::default()
.permissions(permissions)
// `git_dir` is the discovered `.git` directory (or a linked-worktree git dir), so open it
// as-is rather than letting gix append `.git` again.
.open_path_as_is(true)
.with(trust);
Ok(ThreadSafeRepository::open_opts(git_dir, options)?)
}
/// Emulates the result of running `git status` from the command line.
fn status(repo: &Repository, f: impl Fn(Result<FileChange>) -> bool) -> Result<()> {
let work_dir = repo
.workdir()
.ok_or_else(|| anyhow::anyhow!("working tree not found"))?
.to_path_buf();
let status_platform = repo
.status(gix::progress::Discard)?
// Here we discard the `status.showUntrackedFiles` config, as it makes little sense in
// our case to not list new (untracked) files. We could have respected this config
// if the default value weren't `Collapsed` though, as this default value would render
// the feature unusable to many.
.untracked_files(UntrackedFiles::Files)
// Turn on file rename detection, which is off by default.
.index_worktree_rewrites(Some(Rewrites {
copies: None,
percentage: Some(0.5),
limit: 1000,
..Default::default()
}));
// No filtering based on pathView on GitHub (pinned to 079a789e8c)
Solutions
- Open helix in an actual checkout/worktree of that repository instead of the bare directory.
- If the directory is intentionally bare, ignore the error — there is no worktree to diff, so gutter/picker VCS features cannot work.
- For dotfile bare repos, edit the checked-out files in $HOME and keep the bare repo out of your editing root.
Defensive patterns
Strategy: type-guard
Validate before calling
// skip VCS work when the repo is bare (gix::open honours discovery of .git)
if let Ok(repo) = gix::open(cwd) {
if repo.is_bare() { /* no worktree: skip status/diff queries */ }
} Type guard
fn has_worktree(path: &std::path::Path) -> bool {
gix::open(path).map(|repo| !repo.is_bare()).unwrap_or(false)
} Try / catch
// provider callback: distinguish expected bare-repo errors from real ones
f(move |res| match res {
Ok(change) => handle(change),
Err(e) if e.to_string().contains("working tree not found") => true, // expected: bare repo, skip
Err(e) => { log::warn!("vcs: {e}"); true }
}); Prevention
- Root editor sessions in checkouts, not bare repos or .git internals.
- Treat 'working tree not found' as an expected no-op signal in VCS integrations, not a failure to retry.
When it happens
Trigger: Opening helix inside a bare repository directory (dotfiles.git, a server mirror) or inside a repo's .git directory, then opening the changed-files picker or relying on diff signs.
Common situations: Dotfiles managed as a bare repo; exploring mirror/backup repos; accidentally opening .git internals as a project root.
Related errors
AI-assisted analysis of helix-editor/helix@079a789e8c (2026-08-16).
Data as JSON: /api/errors/9a36d96bcb571f50.
Report an issue: GitHub.