GitoxideLabs/gitoxide · error
an undo object ID uses the wrong hash kind
Error message
an undo object ID uses the wrong hash kind
What it means
`parse_state` decodes an `object:<hex>` value in undo metadata with `ObjectId::from_hex`, then verifies the resulting ID's hash kind matches the repository's object hash (`repo.object_hash()`). A mismatch means the metadata was produced for a different hash algorithm (SHA-1 vs SHA-256) and the ID cannot refer to a valid object in this repo.
Solutions
- Regenerate the undo metadata within the current repository so IDs use the repo's hash algorithm.
- Confirm the repo's hash (`git config extensions.objectFormat`) matches the source of the metadata; convert the workflow, not the IDs.
- Do not hand-translate SHA-1 OIDs into SHA-256 — the IDs are content-addressed and cannot be converted; recompute them from the objects.
- If cross-hash metadata must be readable, extend `parse_state` to attempt a lookup instead of a kind check, or store symbolic names instead of raw IDs.
Example fix
// before (sha1 metadata used in a sha256 repo) before = object:1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b // after (regenerate for sha256) before = object:<64-char sha256 oid computed in this repo>
Defensive patterns
Strategy: validation
Validate before calling
fn oid_matches_repo_hash(hex: &[u8], repo: &gix::Repository) -> bool {
gix::hash::ObjectId::from_hex(hex)
.map(|id| id.kind() == repo.object_hash())
.unwrap_or(false)
} Type guard
fn is_sha256_repo(repo: &gix::Repository) -> bool {
repo.object_hash() == gix::hash::Kind::Sha256
} Try / catch
match parse_undo_metadata(&repo, &text) {
Err(e) if e.to_string().contains("an undo object ID uses the wrong hash kind") => {
eprintln!("Metadata was written for a different objectFormat; regenerate it in this repo");
}
res => res?,
} Prevention
- Never copy undo metadata between SHA-1 and SHA-256 repositories
- Check `extensions.objectFormat` before importing metadata from another clone
- Prefer `symbolic:` refs over raw `object:` IDs in portable metadata
- Validate ID length (40 vs 64 hex chars) against the repo hash when generating metadata
When it happens
Trigger: `parse_config` -> `parse_state` (undo.rs:655) encounters `object:` values written as 40-char SHA-1 hex in a SHA-256 repository (or vice versa), so `id.kind() != repo.object_hash()`.
Common situations: Copying undo metadata between repositories created with different `extensions.objectFormat` settings; `git init --object-format=sha256` after metadata was generated for a SHA-1 repo; manually pasting an OID from another clone with a different hash kind.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- an undo ref does not change
- undo metadata has missing, repeated, or unknown keys
- undo/redo cannot delete a worktree HEAD
- a projected worktree reference contains a symbolic cycle
- cannot delete an already-missing reference
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/45d2c817b5904640.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/undo.rs:655
Ok(changes)
}
fn ensure_exact_keys(section: &gix::config::file::SectionRef<'_>, expected: &[&str]) -> Result<()> {
let actual: Vec<_> = section.value_names().collect();
ensure!(
actual == expected,
"undo metadata has missing, repeated, or unknown keys"
);
Ok(())
}
fn parse_state(repo: &gix::Repository, value: &BStr) -> Result<State> {
if value == b"missing" {
return Ok(State::Missing);
}
if let Some(hex) = value.strip_prefix(b"object:") {
let id = ObjectId::from_hex(hex).context("an undo object ID is invalid")?;
ensure!(
id.kind() == repo.object_hash(),
"an undo object ID uses the wrong hash kind"
);
return Ok(State::Object(id));
}
if let Some(name) = value.strip_prefix(b"symbolic:") {
return FullName::try_from(name.as_bstr())
.map(State::Symbolic)
.context("an undo symbolic target is invalid");
}
bail!("an undo reference state has an unknown encoding")
}
fn validate_title(title: &str) -> Result<()> {
ensure!(!title.is_empty(), "an undo operation title cannot be empty");
ensure!(
!title.as_bytes().iter().any(|byte| matches!(byte, b'\n' | b'\r')),
"an undo operation title must be one line"View on GitHub (pinned to e73179060b)