{"record":{"id":"cb9606af28479138","repo":"GitoxideLabs/gitoxide","slug":"undo-reference-sections-are-duplicated-or-out-of-o","errorCode":null,"errorMessage":"undo reference sections are duplicated or out of order","messagePattern":"undo reference sections are duplicated or out of order","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/undo.rs","lineNumber":623,"sourceCode":"        undo.value(\"version\").as_ref().map(|value| value.as_slice()) == Some(VERSION.as_bytes()),\n        \"unsupported undo metadata version\"\n    );\n\n    let mut changes = Vec::new();\n    let mut previous_name: Option<FullName> = None;\n    for section in sections {\n        ensure!(\n            section.header().name() == b\"ref\",\n            \"undo metadata contains an unknown section\"\n        );\n        let subsection = section\n            .header()\n            .subsection_name()\n            .context(\"an undo ref section has no reference name\")?;\n        let name = FullName::try_from(subsection).context(\"an undo entry contains an invalid reference name\")?;\n        ensure!(!is_queue_ref(name.as_bstr()), \"the undo queue records itself\");\n        if let Some(previous) = &previous_name {\n            ensure!(\n                previous < &name,\n                \"undo reference sections are duplicated or out of order\"\n            );\n        }\n        previous_name = Some(name.clone());\n        ensure_exact_keys(&section, &[\"before\", \"after\"])?;\n        let before = section.value(\"before\").context(\"an undo ref has no before-state\")?;\n        let before = parse_state(repo, before.as_bstr())?;\n        let after = section.value(\"after\").context(\"an undo ref has no after-state\")?;\n        let after = parse_state(repo, after.as_bstr())?;\n        ensure!(before != after, \"an undo ref does not change\");\n        changes.push(RefChange { name, before, after });\n    }\n    Ok(changes)\n}\n\nfn ensure_exact_keys(section: &gix::config::file::SectionRef<'_>, expected: &[&str]) -> Result<()> {\n    let actual: Vec<_> = section.value_names().collect();","sourceCodeStart":605,"sourceCodeEnd":641,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/undo.rs#L605-L641","documentation":"Undo metadata lists one `[ref \"<name>\"]` section per changed reference, and these sections must be strictly ordered by reference name with no duplicates. `parse_config` compares each reference name to the previous one and fails when a name is not strictly greater, which indicates either the same reference appears twice or the sections were written out of order.","triggerScenarios":"Parsing undo metadata where two `[ref \"refs/heads/main\"]` sections exist, or where `[ref \"refs/heads/zebra\"]` precedes `[ref \"refs/heads/alpha\"]` — from hand-editing, a faulty writer, or a merge that reordered/duplicated sections.","commonSituations":"Manual edits to the metadata ref, merge conflicts that duplicate or reorder sections, or third-party tooling rewriting the config body without preserving canonical ordering.","solutions":["Sort the `[ref ...]` sections lexicographically by reference name in the metadata","Remove duplicate sections so each reference name appears exactly once","Regenerate the undo metadata by re-running the tix operation that wrote it","Avoid hand-editing undo metadata; let tix serialize it"],"exampleFix":"// before (duplicated and out of order)\n[ref \"refs/heads/zebra\"]\n\tbefore = missing\n\tafter = object:111\n[ref \"refs/heads/alpha\"]\n\tbefore = missing\n\tafter = object:222\n[ref \"refs/heads/zebra\"]\n\tbefore = object:111\n\tafter = object:333\n// after\n[ref \"refs/heads/alpha\"]\n\tbefore = missing\n\tafter = object:222\n[ref \"refs/heads/zebra\"]\n\tbefore = missing\n\tafter = object:333","handlingStrategy":"validation","validationCode":"// verify ref sections are unique and sorted before parsing\nlet config = gix::config::File::try_from(body.as_ref())?;\nlet mut names: Vec<_> = config.sections()\n    .filter(|s| s.header().name() == b\"ref\")\n    .filter_map(|s| s.header().subsection_name().map(|n| n.to_vec()))\n    .collect();\nlet sorted = names.clone();\nnames.sort();\nnames.dedup();\nassert_eq!(names.len(), sorted.len(), \"duplicate ref sections\");\nassert_eq!(sorted, names, \"ref sections out of order\");","typeGuard":"fn sections_sorted_unique(names: &[Vec<u8>]) -> bool {\n    names.windows(2).all(|w| w[0] < w[1])\n}","tryCatchPattern":"match parse_undo_metadata(&repo, &body) {\n    Ok(changes) => changes,\n    Err(e) if e.to_string().contains(\"duplicated or out of order\") => {\n        eprintln!(\"undo metadata malformed; regenerating\");\n        regenerate_undo_metadata(&repo)?\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Let tix serialize undo metadata; never reorder sections by hand","Resolve merge conflicts in the metadata by regenerating, not manual editing","Validate ordering after any tool rewrites config-style files","Keep one writer per undo metadata ref to avoid concurrent rewrites"],"tags":["git","undo","validation","ordering"],"backgroundTag":"schema-validation-failed","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}