GitoxideLabs/gitoxide · error

undo metadata contains an unknown section

Error message

undo metadata contains an unknown section

What it means

When tix restores or inspects undo state, it parses the stored undo metadata as a Git config file. After the leading `[undo]` version section, every remaining section must be a `[ref "<name>"]` section describing one reference change. This error is raised when any subsequent section has a header name other than `ref`, meaning the metadata file was hand-edited, corrupted, or written by an incompatible version of tix.

Solutions

  1. Inspect the undo metadata and remove or fix any section whose header is not `[ref "<reference-name>"]`
  2. Regenerate the undo metadata by re-running the operation that produced it, or clear the undo state and redo the ref changes manually
  3. Check for merge-conflict markers in the metadata if it was touched by a merge
  4. Ensure all tix versions used against the repository are from the same compatible release

Example fix

// before (corrupt metadata body)
[undo]
	version = v1
[ref "refs/heads/main"]
	before = missing
	after = object:abc123
[stale]
	key = value
// after
[undo]
	version = v1
[ref "refs/heads/main"]
	before = missing
	after = object:abc123
Defensive patterns

Strategy: validation

Validate before calling

// before reading undo state, validate the metadata body
let config = gix::config::File::try_from(body.as_ref())?;
let mut sections = config.sections();
let undo = sections.next().ok_or("missing [undo] header")?;
assert_eq!(undo.header().name(), b"undo");
for s in sections {
    assert_eq!(s.header().name(), b"ref", "unknown section {:?}", s.header().name());
}

Type guard

fn is_ref_section(header: &gix::config::file::Header<'_>) -> bool {
    header.name() == b"ref" && header.subsection_name().is_some()
}

Try / catch

match load_undo_state(&repo) {
    Ok(changes) => apply(changes),
    Err(e) if e.to_string().contains("unknown section") => {
        eprintln!("undo metadata corrupt; resetting undo state");
        reset_undo_state(&repo)?;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling any undo/redo flow that reaches `parse_config` (via `parse_commit`) while the stored undo metadata body contains a section like `[other]` or `[refextra "..."]` after the `[undo]` header — typically from manual editing of the metadata ref, a merge conflict inside it, or tampering.

Common situations: A developer edits the undo metadata ref (or a merge leaves conflict markers in it), an old tix version wrote a different section layout, or an external tool rewrote the refs storage.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/0a87e05249ea0e08. Report an issue: GitHub.

Appendix: source

Thrown at gix-tix/src/edit/undo.rs:612

fn parse_config(repo: &gix::Repository, body: &BStr) -> Result<Vec<RefChange>> {
    let config = File::try_from(body).context("could not parse undo metadata as Git config")?;
    let mut sections = config.sections();
    let undo = sections.next().context("undo metadata has no version section")?;
    ensure!(
        undo.header().name() == b"undo" && undo.header().subsection_name().is_none(),
        "undo metadata must start with [undo]"
    );
    ensure_exact_keys(&undo, &["version"])?;
    ensure!(
        undo.value("version").as_ref().map(|value| value.as_slice()) == Some(VERSION.as_bytes()),
        "unsupported undo metadata version"
    );

    let mut changes = Vec::new();
    let mut previous_name: Option<FullName> = None;
    for section in sections {
        ensure!(
            section.header().name() == b"ref",
            "undo metadata contains an unknown section"
        );
        let subsection = section
            .header()
            .subsection_name()
            .context("an undo ref section has no reference name")?;
        let name = FullName::try_from(subsection).context("an undo entry contains an invalid reference name")?;
        ensure!(!is_queue_ref(name.as_bstr()), "the undo queue records itself");
        if let Some(previous) = &previous_name {
            ensure!(
                previous < &name,
                "undo reference sections are duplicated or out of order"
            );
        }
        previous_name = Some(name.clone());
        ensure_exact_keys(&section, &["before", "after"])?;
        let before = section.value("before").context("an undo ref has no before-state")?;

View on GitHub (pinned to e73179060b)