GitoxideLabs/gitoxide · error
could not decode commit-graph parent
Error message
could not decode commit-graph parent: {err} What it means
When building history from cached commits, tix reads each commit's parents from the commit-graph. If decoding a parent entry from the commit-graph fails (corrupt or truncated graph file, hash mismatch, unsupported format), the error is wrapped with this message and propagated to callers like `for_commits`, `schedule_cached`, and `load`.
Solutions
- Delete the commit-graph file(s) (`.git/objects/info/commit-graph` and the commit-graphs directory) and regenerate with `git commit-graph write` or `gix` equivalent
- Run `git fsck` to detect broader object-store corruption and restore from a clone/backup if needed
- Re-clone or restore the repository from a known-good source if the graph and objects are both damaged
- Verify the repository's object hash matches the commit-graph format in use
Example fix
// recovery (shell) rm -rf .git/objects/info/commit-graph .git/objects/info/commit-graphs git commit-graph write --reachable
Defensive patterns
Strategy: fallback
Validate before calling
// check commit-graph health before cached traversal
if std::path::Path::new(".git/objects/info/commit-graph").exists() {
let ok = std::process::Command::new("git")
.args(["commit-graph", "verify"])
.status()
.map(|s| s.success())
.unwrap_or(false);
if !ok {
eprintln!("commit-graph corrupt; regenerating");
let _ = std::fs::remove_file(".git/objects/info/commit-graph");
}
} Type guard
fn commit_graph_is_valid(repo_path: &std::path::Path) -> bool {
std::process::Command::new("git")
.args(["-C"])
.arg(repo_path)
.args(["commit-graph", "verify"])
.status()
.map(|s| s.success())
.unwrap_or(false)
} Try / catch
match load_history(&repo) {
Ok(h) => h,
Err(e) if e.to_string().contains("commit-graph parent") => {
eprintln!("commit-graph unreadable; falling back to object walk");
remove_commit_graph(&repo)?;
load_history(&repo)?
}
Err(e) => return Err(e),
} Prevention
- Run `git commit-graph verify` after crashes or disk-full events
- Avoid interrupting `git commit-graph write`
- Regenerate the commit-graph after copying repositories
- Monitor `git fsck` output for object-store damage
When it happens
Trigger: Calling history loading/scheduling APIs (`for_commits`, `schedule_cached`, `load`) while the repository's commit-graph file is corrupt, truncated, or contains a parent position that cannot be decoded for the traversed commit.
Common situations: A crash or disk-full event corrupted `.git/objects/info/commit-graph`, an interrupted `git commit-graph write`, a version mismatch between the graph file and the object hash, or a repository copied incompletely.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- the undo queue first-parent chain contains a cycle
- At least one object couldn't be looked up even though it…
- could not read reference
- history traversal did not produce a graph
- an undo queue commit does not use the empty tree
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/73ec6bd0d1369d1f.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/history.rs:404
for token in iter {
match token.context("could not decode cached history commit")? {
Token::Tree { .. } => {}
Token::Parent { id } => parents.push(id),
Token::Committer { signature } => {
commit_time = signature.seconds();
break;
}
_ => {}
}
}
(parents, commit_time, None)
}
gix::traverse::commit::Either::CachedCommit(commit) => {
let cache = cache.expect("cached commits originate from the provided commit-graph");
let mut parents = gix::traverse::commit::ParentIds::new();
for parent in commit.iter_parents() {
let parent =
parent.map_err(|err| anyhow::anyhow!("could not decode commit-graph parent: {err}"))?;
parents.push(cache.id_at(parent).to_owned());
}
(
parents,
commit.committer_timestamp() as gix::date::SecondsSinceUnixEpoch,
Some(commit.generation()),
)
}
};
if shallow.contains(&id) {
parents.clear();
}
let parents: Vec<_> = parents
.into_iter()
.map(|parent| self.intern(parent))
.collect::<Result<_>>()?;
let start: u32 = self
.parentsView on GitHub (pinned to e73179060b)