GitoxideLabs/gitoxide · error
find returned a cached commit, so we expect cache to be…
Error message
find returned a cached commit, so we expect cache to be present
What it means
This is an expect() panic in gix-blame's collect_parents: when the commit graph returns a CachedCommit variant, the function requires a parallel commit cache (Graph cache) to have been provided. The library assumes that whenever a cached commit is produced, the cache must exist; if the cache is None while the iterator yields CachedCommit, the assumption is violated and it panics instead of returning a recoverable error.
Solutions
- Ensure the commit cache is created and passed to the blame traversal whenever the graph may yield cached commits
- Inspect the call site in gix-blame/src/file/function.rs collect_parents and make cache retrieval fallible (return an Error) instead of expect
- Update gix-blame if this is a known bug fixed in a newer release
- File a bug with a reproduction, since a panic here indicates an internal invariant violation
Example fix
// before
let cache = cache
.as_ref()
.expect("find returned a cached commit, so we expect cache to be present");
// after
let cache = cache.as_ref().ok_or_else(|| {
message("cached commit produced without a commit cache being present")
})?; Defensive patterns
Strategy: try-catch
Validate before calling
// Before invoking blame internals, ensure a cache is supplied when cached commits are possible: assert!(cache.is_some(), "commit cache must be provided for cached-commit traversal");
Type guard
fn cache_present(cache: &Option<Cache>) -> bool { cache.is_some() } Try / catch
// Panics are not catchable safely in Rust; guard instead:
let cache = match cache.as_ref() {
Some(c) => c,
None => return Err(message("cached commit without cache")),
}; Prevention
- Always construct the blame traversal with its commit cache wired in
- Prefer fallible ok_or over expect when threading optional handles
- Run blame against a fixture repo in CI to catch regressions
- Update gix-blame regularly to pick up invariant fixes
When it happens
Trigger: Calling the blame file API with a commit iterator/graph configured so that Either::CachedCommit is produced while the `cache` parameter is None — e.g. a miswired internal combination of traversal options where the cache handle is not passed down.
Common situations: Hit by library maintainers or users constructing the blame traversal directly with inconsistent graph/cache arguments, or after an upstream refactor changed how the commit cache is threaded through, rather than by ordinary end users of the porcelain API.
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
- visit_non_tree() called us
- BUG: ResolvedSignatures don't exist here when nothing is set
- initial hunks are never ancestors
- BUG: hunks are never zero-sized
- populated above
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/372d0a3fd4ef367c.
Report an issue: GitHub.
Appendix: source
Thrown at gix-blame/src/file/function.rs:873
)?;
stats.trees_decoded -= 1;
Ok(res.map(|e| e.oid))
}
type ParentIds = SmallVec<[(gix_hash::ObjectId, i64); 2]>;
fn collect_parents(
commit: gix_traverse::commit::Either<'_, '_>,
odb: &impl gix_object::Find,
cache: Option<&gix_commitgraph::Graph>,
buf: &mut Vec<u8>,
) -> Result<ParentIds, Error> {
let mut parent_ids: ParentIds = Default::default();
match commit {
gix_traverse::commit::Either::CachedCommit(commit) => {
let cache = cache
.as_ref()
.expect("find returned a cached commit, so we expect cache to be present");
for parent_pos in commit.iter_parents() {
let parent = cache.commit_at(parent_pos?);
parent_ids.push((parent.id().to_owned(), parent.committer_timestamp() as i64));
}
}
gix_traverse::commit::Either::CommitRefIter(commit_ref_iter) => {
for id in commit_ref_iter.parent_ids() {
let parent = odb.find_commit_iter(id.as_ref(), buf).ok();
let parent_commit_time = parent
.and_then(|parent| parent.committer().ok().map(|committer| committer.seconds()))
.unwrap_or_default();
parent_ids.push((id, parent_commit_time));
}
}
}
Ok(parent_ids)
}
View on GitHub (pinned to e73179060b)