gitbutlerapp/gitbutler · error
Refusing to read Git metadata path
Error message
Refusing to read Git metadata path '{}' What it means
read_worktree_file validates path components with gix and refuses any path that resolves into the .git directory (gix::validate::path::component::Error::DotGitDir). Reading Git metadata files through this API is not allowed.
Solutions
- Exclude .git (and nested git dirs) from the paths you request.
- Read Git metadata through dedicated git APIs (repo config, refs) instead of file reads.
- Filter directory listings before requesting file contents.
Example fix
// before
read_file_from_workspace(project, ".git/config")
// after
if path.components().any(|c| c.as_os_str() == ".git") { return Err(...); }
read_file_from_workspace(project, path) Defensive patterns
Strategy: validation
Validate before calling
fn touches_git_dir(p: &Path) -> bool {
p.components().any(|c| c.as_os_str() == ".git")
}
if touches_git_dir(path) { return Err("git metadata paths are not readable here"); } Type guard
fn is_git_metadata(p: &Path) -> bool {
p.components().any(|c| c.as_os_str() == ".git")
} Try / catch
match result {
Err(e) if e.to_string().contains("Refusing to read Git metadata path") => {
// skip this path in listings
}
other => other,
} Prevention
- Filter .git entries out of any directory listing fed to file reads.
- Use dedicated git APIs for metadata (config, refs, objects).
- Apply this check to nested worktrees' .git files too.
When it happens
Trigger: Calling read_worktree_file for a path inside .git (e.g. ".git/config", ".git/HEAD", ".git/objects/...") via get_workspace_file_from_source or read_file_from_workspace.
Common situations: A file watcher or UI listing the worktree includes .git contents and something tries to display them; tooling asking to read .git/COMMIT_EDITMSG through the workspace-file API.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- Refusing to read ' ' from commit as it's not relative to…
- Path to read from at
- Refusing to read Git-ignored path
- another pre-commit hook is already using the repository…
- askpass broker must be initialized
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/81fcf9afb859dbd7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/gitbutler-repo/src/commands.rs:393
canonical_workdir.display()
);
}
};
// Refuse `.git` in every spelling an OS can map onto it (`.GIT`,
// `GIT~1`, `.git.`). Runs before the stat: `.git` can be a file
// (linked worktrees) and must be refused all the same.
if relative_path.components().any(|component| {
matches!(
gix::validate::path::component(
component.as_os_str().as_encoded_bytes().as_bstr(),
None,
Default::default(),
),
Err(gix::validate::path::component::Error::DotGitDir)
)
}) {
bail!(
"Refusing to read Git metadata path '{}'",
relative_path.display()
);
}
let out = match path.symlink_metadata() {
Ok(md) => {
// Directories are exempt: their `FileInfo` placeholder carries no
// content, and callers rely on getting it rather than an error.
if !md.is_dir() {
ensure_not_ignored(repo, &relative_path)?;
}
if md.is_file() {
let content = std::fs::read(&path)?;
FileInfo::from_content(&relative_path, &content)
} else if md.is_symlink() {
let content = std::fs::read_link(&path)?;View on GitHub (pinned to 58e5313667)