GitoxideLabs/gitoxide · warning
prefix-stripping cannot fail as base is within our root
Error message
prefix-stripping cannot fail as base is within our root
What it means
A Rust `expect()` panic in the loose-refs directory iterator's `next()` (gix-ref/store/file/loose/iter.rs). For each file entry it strips the store's `base` directory prefix from the entry path, asserting the entry is always inside the base since both come from walking the same root. It panics if `self.base` and the walked root diverge — e.g. the base path differs from the directory handed to `std::fs::read_dir` (symlink resolution differences, non-canonicalized paths like `./refs` vs `refs`, or path casing/format mismatches on Windows).
Solutions
- Canonicalize both the base and the directory before constructing the iterator (`std::fs::canonicalize`) so `strip_prefix` matches.
- Ensure the exact same `PathBuf` value is used for the base and the traversal root — avoid rebuilding the path separately.
- Resolve symlinks (especially symlinked `.git` or `refs` directories) before iteration.
- If it occurs on canonicalized paths, report upstream with the OS and path strings — internal invariant violation.
Example fix
// before
let base = PathBuf::from("./.git/refs");
let iter = loose::Iter::at(dir_read_from_symlinked_path, base, ...); // panic
// after
let base = std::fs::canonicalize("./.git/refs")?;
let iter = loose::Iter::at(base.clone(), base, ...); // identical canonical paths Defensive patterns
Strategy: validation
Validate before calling
let base = std::fs::canonicalize(".git/refs")?;
assert_eq!(base, dir_being_walked_canonicalized); // base must equal traversal root Type guard
fn same_root(base: &Path, root: &Path) -> bool {
std::fs::canonicalize(base).ok().as_deref() == std::fs::canonicalize(root).ok().as_deref()
} Prevention
- Canonicalize paths before constructing loose iterators
- Use the identical PathBuf for base and traversal root
- Resolve symlinked .git directories up front
When it happens
Trigger: Creating a `loose::Iter` with a `base` that is not the canonicalized same path as the directory being traversed (e.g. base contains `..`, a symlink, or a trailing separator while read_dir resolves differently); platform-specific path normalization (`\\?\` prefixes, case differences) breaking `strip_prefix`.
Common situations: Passing relative or non-canonical repository paths (`./.git/refs`, symlinked `.git` dirs) into low-level loose-ref iteration; using a different path form for base vs the traversal root; Windows path quirks.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- prior validation
- a parent is always there unless empty
- ' ' is not a valid configuration key
- Cannot use iter_v1() on index of type
- Cannot use iter_v2() on index of type
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/fd2eec3276a14302.
Report an issue: GitHub.
Appendix: source
Thrown at gix-ref/src/store/file/loose/iter.rs:59
}),
}
}
}
impl Iterator for SortedLoosePaths {
type Item = std::io::Result<(PathBuf, FullName)>;
fn next(&mut self) -> Option<Self::Item> {
for entry in self.file_walk.as_mut()?.by_ref() {
match entry {
Ok(entry) => {
if !entry.file_type().is_ok_and(|ft| ft.is_file()) {
continue;
}
let full_path = entry.path().into_owned();
let full_name = full_path
.strip_prefix(&self.base)
.expect("prefix-stripping cannot fail as base is within our root");
let Ok(full_name) = gix_path::try_into_bstr(full_name)
.map(|name| gix_path::to_unix_separators_on_windows(name).into_owned())
else {
continue;
};
if let Some(prefix) = &self.prefix
&& !full_name.starts_with(prefix)
{
continue;
}
if let Some(suffix) = &self.suffix
&& !full_name.ends_with(suffix)
{
continue;
}
if gix_validate::reference::name_partial(full_name.as_bstr()).is_ok() {
let name = FullName(full_name);
return Some(Ok((full_path, name)));View on GitHub (pinned to e73179060b)