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 path

View on GitHub (pinned to 079a789e8c)

Solutions

  1. Open helix in an actual checkout/worktree of that repository instead of the bare directory.
  2. If the directory is intentionally bare, ignore the error — there is no worktree to diff, so gutter/picker VCS features cannot work.
  3. 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

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.