GitoxideLabs/gitoxide · error
duplicate Todo header
Error message
duplicate Todo header
What it means
The edit document must contain the `Todo` enrichment header at most once. `parse` tracks this with a `todo_seen` flag and throws this error when a second `Todo` line appears, because duplicate headers would make the enrichment state ambiguous.
Solutions
- Remove duplicate `Todo` lines so the document contains exactly one.
- Regenerate the document with `document()` rather than concatenating or re-appending headers.
- When programmatically editing, check for an existing `Todo` header before inserting one.
Example fix
// before (duplicated) Todo Todo pick abc123 message // after Todo pick abc123 message
Defensive patterns
Strategy: validation
Validate before calling
fn todo_header_count(body: &[&[u8]]) -> usize {
body.iter().filter(|l| *l == &TODO).count()
} Try / catch
match parse(&edited) {
Ok(doc) => doc,
Err(e) if e.to_string().contains("duplicate Todo header") => {
eprintln!("Edit document malformed; regenerating.");
regenerate_and_parse(repo, old_id)
}
Err(e) => return Err(e),
} Prevention
- When appending headers programmatically, check for existing ones first.
- Never concatenate two edited documents.
- Regenerate with `document()` when in doubt about document structure.
When it happens
Trigger: Calling `parse` (from `apply_conflict_reporting` or the parse tests) on a document body that contains the `Todo` header line two or more times, where non-comment lines are compared literally against the `TODO` marker.
Common situations: A tool or script that appends enrichment headers without checking existing ones; a merge of two edited documents; manual duplication while editing the todo file.
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
- duplicate Message header
- the edited commit message is empty
- CommentChar must not contain a line ending
- unknown commit header
- lines in ' ' could not be parsed
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/c4ffb7cfdb1e9fc8.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/reword.rs:379
let remainder = parts.next().context("the enrichment headers are missing")?;
let mut enrichment = crate::enrich::Headers::default();
let mut todo_seen = false;
let mut message_seen = false;
let mut message_offset = None;
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),View on GitHub (pinned to e73179060b)