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

  1. Regenerate the undo metadata within the current repository so IDs use the repo's hash algorithm.
  2. Confirm the repo's hash (`git config extensions.objectFormat`) matches the source of the metadata; convert the workflow, not the IDs.
  3. Do not hand-translate SHA-1 OIDs into SHA-256 — the IDs are content-addressed and cannot be converted; recompute them from the objects.
  4. 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

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


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)