gitbutlerapp/gitbutler · error · anyhow::Error
MCP root is not a file URI: {}
Error message
MCP root is not a file URI: {} What it means
When the but MCP server resolves the repository from the client's filesystem roots, each root URI is parsed and converted to a local path with Url::to_file_path(). Only file:// URIs can be converted; any other scheme (http, vscode-remote, untitled, ...) fails for that root.
Source
Thrown at crates/but/src/command/mcp/mod.rs:545
}
};
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()))?;
let repository = ctx
.workdir_or_gitdir()
.context("Could not determine the repository root")?
.canonicalize()
.context("Could not resolve the repository root")?;
let name = repository
.file_name()
.and_then(|name| name.to_str())
.unwrap_or("repository")
.to_owned();View on GitHub (pinned to caf1f223d3)
Solutions
- Open or attach the local directory in the client so at least one root is a file:// URI
- Add the local repository checkout as an additional workspace folder
- Run the MCP client on the machine where the repository actually lives
Defensive patterns
Strategy: validation
Validate before calling
const isFileRoot = (root: { uri: string }) => root.uri.startsWith('file://');
// client side: only send local roots
const roots = workspaceFolders.filter(isFileRoot); Type guard
const isFileRoot = (r: { uri: string }): r is { uri: `file://${string}` } =>
r.uri.startsWith('file://'); Try / catch
Server side: skip non-file roots with a debug log instead of erroring; only fail with the aggregated 'no repository found' message when no file root identifies a repo (root_path's caller already aggregates failures).
Prevention
- Send at least one local file:// root from the client
- Filter non-file URIs client-side before connecting in remote-IDE setups
When it happens
Trigger: An MCP client sends workspace roots whose URI scheme is not file, e.g. vscode-remote:// for Remote-SSH/devcontainers, untitled:, or http(s) URLs, while but iterates roots to find a git repository.
Common situations: IDE remoting (SSH, devcontainer, web IDE) where the workspace folder is not a local path; clients that include virtual documents as roots.
Related errors
- textFromToolResult(result)
- The refreshed reviews were missing from the response.
- The updated review was missing from the response.
- textFromToolResult(result)
- The detail result was missing structured data.
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/8a7d393a2d7be2c5.
Report an issue: GitHub.