{"record":{"id":"45d2c817b5904640","repo":"GitoxideLabs/gitoxide","slug":"an-undo-object-id-uses-the-wrong-hash-kind","errorCode":null,"errorMessage":"an undo object ID uses the wrong hash kind","messagePattern":"an undo object ID uses the wrong hash kind","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/undo.rs","lineNumber":655,"sourceCode":"    Ok(changes)\n}\n\nfn ensure_exact_keys(section: &gix::config::file::SectionRef<'_>, expected: &[&str]) -> Result<()> {\n    let actual: Vec<_> = section.value_names().collect();\n    ensure!(\n        actual == expected,\n        \"undo metadata has missing, repeated, or unknown keys\"\n    );\n    Ok(())\n}\n\nfn parse_state(repo: &gix::Repository, value: &BStr) -> Result<State> {\n    if value == b\"missing\" {\n        return Ok(State::Missing);\n    }\n    if let Some(hex) = value.strip_prefix(b\"object:\") {\n        let id = ObjectId::from_hex(hex).context(\"an undo object ID is invalid\")?;\n        ensure!(\n            id.kind() == repo.object_hash(),\n            \"an undo object ID uses the wrong hash kind\"\n        );\n        return Ok(State::Object(id));\n    }\n    if let Some(name) = value.strip_prefix(b\"symbolic:\") {\n        return FullName::try_from(name.as_bstr())\n            .map(State::Symbolic)\n            .context(\"an undo symbolic target is invalid\");\n    }\n    bail!(\"an undo reference state has an unknown encoding\")\n}\n\nfn validate_title(title: &str) -> Result<()> {\n    ensure!(!title.is_empty(), \"an undo operation title cannot be empty\");\n    ensure!(\n        !title.as_bytes().iter().any(|byte| matches!(byte, b'\\n' | b'\\r')),\n        \"an undo operation title must be one line\"","sourceCodeStart":637,"sourceCodeEnd":673,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/undo.rs#L637-L673","documentation":"`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.","triggerScenarios":"`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()`.","commonSituations":"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.","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."],"exampleFix":"// before (sha1 metadata used in a sha256 repo)\nbefore = object:1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b\n// after (regenerate for sha256)\nbefore = object:<64-char sha256 oid computed in this repo>","handlingStrategy":"validation","validationCode":"fn oid_matches_repo_hash(hex: &[u8], repo: &gix::Repository) -> bool {\n    gix::hash::ObjectId::from_hex(hex)\n        .map(|id| id.kind() == repo.object_hash())\n        .unwrap_or(false)\n}","typeGuard":"fn is_sha256_repo(repo: &gix::Repository) -> bool {\n    repo.object_hash() == gix::hash::Kind::Sha256\n}","tryCatchPattern":"match parse_undo_metadata(&repo, &text) {\n    Err(e) if e.to_string().contains(\"an undo object ID uses the wrong hash kind\") => {\n        eprintln!(\"Metadata was written for a different objectFormat; regenerate it in this repo\");\n    }\n    res => res?,\n}","preventionTips":["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"],"tags":["hash-mismatch","sha1","sha256","config-parse","undo"],"backgroundTag":"invalid-config-value","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}