gitbutlerapp/gitbutler · error

None of the MCP client's filesystem roots identify a Git rep

Error message

None of the MCP client's filesystem roots identify a Git repository. {}

What it means

After probing every root with open_repository (crates/but/src/command/mcp/mod.rs:535), none opened as a Git repository (or the root URI was not a valid file URI); each failure string is collected and joined into the message. The client's roots exist but none is, or sits above, a usable .git worktree.

Source

Thrown at crates/but/src/command/mcp/mod.rs:535

        );
    }

    let mut failures = Vec::new();
    for root in roots {
        let path = match root_path(root) {
            Ok(path) => path,
            Err(err) => {
                failures.push(err.to_string());
                continue;
            }
        };
        match open_repository(&path) {
            Ok(resolved) => return Ok(resolved),
            Err(err) => failures.push(format!("{}: {err:#}", path.display())),
        }
    }

    bail!(
        "None of the MCP client's filesystem roots identify a Git repository. {}",
        failures.join("; ")
    )
}

fn root_path(root: &Root) -> Result<PathBuf> {
    let url = Url::parse(&root.uri)
        .with_context(|| format!("MCP root is not a valid URI: {}", root.uri))?;
    url.to_file_path()
        .map_err(|()| anyhow::anyhow!("MCP root is not a file URI: {}", root.uri))
}

fn open_repository(repository: &Path) -> Result<ResolvedRepository> {
    let repository = repository
        .canonicalize()
        .with_context(|| format!("Could not resolve repository at {}", repository.display()))?;
    let ctx = but_ctx::Context::discover(&repository)
        .with_context(|| format!("Could not open repository at {}", repository.display()))?;

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Add a directory that is (or contains) a Git worktree as a root.
  2. Pass the `repository` path argument directly.
  3. Read the joined failure list in the message to see which root failed and why (invalid URI vs. not a repository).

Example fix

// before: root = file:///home/user/documents (no repo)

// after: root = file:///home/user/documents/project (contains .git)
// or
{ "repository": "/home/user/documents/project", ... }
Defensive patterns

Strategy: validation

Validate before calling

for root in roots {
    let path = Url::parse(&root.uri)?.to_file_path().map_err(|()| anyhow::anyhow!("not a file URI"))?;
    if gix::open(&path).map(|r| r.is_worktree()).unwrap_or(false) {
        return Ok(path); // a probe-able root exists
    }
}
anyhow::bail!("no root is a git repository; pass repository explicitly");

Try / catch

match resolve_repository(None, context).await {
    Err(err) if err.to_string().contains("identify a Git repository") => {
        // parse the per-root failure details and retry with an explicit path
    }
    other => other,
}

Prevention

When it happens

Trigger: All workspace roots are plain directories with no discoverable .git at or above them; a root is a non-file URI (remote scheme) that Url::to_file_path rejects; sandboxing hides .git from the server.

Common situations: Opened a parent folder of several projects where none is at a root level; cloud-hosted root URIs; permissions that block .git discovery.

Related errors


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