{"record":{"id":"c4ffb7cfdb1e9fc8","repo":"GitoxideLabs/gitoxide","slug":"duplicate-todo-header","errorCode":null,"errorMessage":"duplicate Todo header","messagePattern":"duplicate Todo header","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/reword.rs","lineNumber":379,"sourceCode":"    let remainder = parts.next().context(\"the enrichment headers are missing\")?;\n    let mut enrichment = crate::enrich::Headers::default();\n    let mut todo_seen = false;\n    let mut message_seen = false;\n    let mut message_offset = None;\n    let mut consumed = 0;\n    for line in remainder.lines_with_terminator() {\n        consumed += line.len();\n        let line = trim_cr(line.strip_suffix(b\"\\n\").unwrap_or(line));\n        if line.is_empty() {\n            message_offset = Some(consumed);\n            break;\n        }\n        if line.starts_with(comment_char) {\n            continue;\n        }\n        if line == TODO {\n            if std::mem::replace(&mut todo_seen, true) {\n                anyhow::bail!(\"duplicate Todo header\");\n            }\n            enrichment.todo = true;\n        } else if let Some(title) = line.strip_prefix(MESSAGE) {\n            if std::mem::replace(&mut message_seen, true) {\n                anyhow::bail!(\"duplicate Message header\");\n            }\n            let title = title.trim();\n            enrichment.message = (!title.is_empty()).then(|| title.into());\n        } else {\n            anyhow::bail!(\"unknown commit header: {}\", line.as_bstr());\n        }\n    }\n    let message_offset = message_offset.context(\"expected an empty line after the commit headers\")?;\n    let message = cleanup_message(\n        remainder\n            .get(message_offset..)\n            .context(\"the commit message is missing\")?,\n        Some(comment_char),","sourceCodeStart":361,"sourceCodeEnd":397,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/reword.rs#L361-L397","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (duplicated)\nTodo\nTodo\npick abc123 message\n// after\nTodo\npick abc123 message","handlingStrategy":"validation","validationCode":"fn todo_header_count(body: &[&[u8]]) -> usize {\n    body.iter().filter(|l| *l == &TODO).count()\n}","typeGuard":null,"tryCatchPattern":"match parse(&edited) {\n    Ok(doc) => doc,\n    Err(e) if e.to_string().contains(\"duplicate Todo header\") => {\n        eprintln!(\"Edit document malformed; regenerating.\");\n        regenerate_and_parse(repo, old_id)\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["When appending headers programmatically, check for existing ones first.","Never concatenate two edited documents.","Regenerate with `document()` when in doubt about document structure."],"tags":["git","parsing","duplicate-header","reword","validation"],"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"}