GitoxideLabs/gitoxide · error

duplicate Message header

Error message

duplicate Message header

What it means

The edit document must contain the `Message` header at most once. `parse` uses a `message_seen` flag and bails with this error if a second `Message` line is encountered, since the title/enrichment could not be resolved unambiguously.

Solutions

  1. Keep exactly one `Message` header; delete the extra one.
  2. Move message content into the free-form message section below the headers instead of adding more `Message` lines.
  3. Regenerate the document via `document()` to get a well-formed header block.

Example fix

// before
Message: first title
Message: second title
// after
Message: first title
Defensive patterns

Strategy: validation

Validate before calling

fn message_header_count(body: &[&[u8]]) -> usize {
    body.iter().filter(|l| l.starts_with(MESSAGE)).count()
}

Try / catch

match parse(&edited) {
    Ok(doc) => doc,
    Err(e) if e.to_string().contains("duplicate Message header") => {
        eprintln!("Keep exactly one Message header; extra lines belong in the message body.");
        Err(e)
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `parse` (from `apply_conflict_reporting` or the parse tests) on a document whose body contains two or more lines starting with the `MESSAGE` prefix.

Common situations: Scripts appending an extra `Message:` line; merged or concatenated edit documents; a user pasting the message twice in the editor in header position.

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/5be0f01ee07fd296. Report an issue: GitHub.

Appendix: source

Thrown at gix-tix/src/edit/reword.rs:384

    let mut consumed = 0;
    for line in remainder.lines_with_terminator() {
        consumed += line.len();
        let line = trim_cr(line.strip_suffix(b"\n").unwrap_or(line));
        if line.is_empty() {
            message_offset = Some(consumed);
            break;
        }
        if line.starts_with(comment_char) {
            continue;
        }
        if line == TODO {
            if std::mem::replace(&mut todo_seen, true) {
                anyhow::bail!("duplicate Todo header");
            }
            enrichment.todo = true;
        } else if let Some(title) = line.strip_prefix(MESSAGE) {
            if std::mem::replace(&mut message_seen, true) {
                anyhow::bail!("duplicate Message header");
            }
            let title = title.trim();
            enrichment.message = (!title.is_empty()).then(|| title.into());
        } else {
            anyhow::bail!("unknown commit header: {}", line.as_bstr());
        }
    }
    let message_offset = message_offset.context("expected an empty line after the commit headers")?;
    let message = cleanup_message(
        remainder
            .get(message_offset..)
            .context("the commit message is missing")?,
        Some(comment_char),
    );
    Ok(Edit {
        author,
        author_time,
        committer,

View on GitHub (pinned to e73179060b)