{"record":{"id":"f2a825bc453a12a5","repo":"GitoxideLabs/gitoxide","slug":"an-undo-operation-title-must-be-one-line","errorCode":null,"errorMessage":"an undo operation title must be one line","messagePattern":"an undo operation title must be one line","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/undo.rs","lineNumber":671,"sourceCode":"    if let Some(hex) = value.strip_prefix(b\"object:\") {\n        let id = ObjectId::from_hex(hex).context(\"an undo object ID is invalid\")?;\n        ensure!(\n            id.kind() == repo.object_hash(),\n            \"an undo object ID uses the wrong hash kind\"\n        );\n        return Ok(State::Object(id));\n    }\n    if let Some(name) = value.strip_prefix(b\"symbolic:\") {\n        return FullName::try_from(name.as_bstr())\n            .map(State::Symbolic)\n            .context(\"an undo symbolic target is invalid\");\n    }\n    bail!(\"an undo reference state has an unknown encoding\")\n}\n\nfn validate_title(title: &str) -> Result<()> {\n    ensure!(!title.is_empty(), \"an undo operation title cannot be empty\");\n    ensure!(\n        !title.as_bytes().iter().any(|byte| matches!(byte, b'\\n' | b'\\r')),\n        \"an undo operation title must be one line\"\n    );\n    Ok(())\n}\n\nfn retention_parents(repo: &gix::Repository, predecessor: ObjectId, changes: &[RefChange]) -> Result<Vec<ObjectId>> {\n    let mut parents = vec![predecessor];\n    let mut seen = HashSet::from([predecessor]);\n    for id in changes\n        .iter()\n        .flat_map(|change| [&change.before, &change.after])\n        .filter_map(|state| match state {\n            State::Object(id) => Some(*id),\n            State::Missing | State::Symbolic(_) => None,\n        })\n    {\n        if seen.contains(&id) {","sourceCodeStart":653,"sourceCodeEnd":689,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/undo.rs#L653-L689","documentation":"`validate_title` requires undo operation titles to be exactly one line. Commit titles cannot contain line breaks (the title is the first line of the commit message), so the library rejects titles containing `\\n` or `\\r` bytes rather than silently truncating them.","triggerScenarios":"Calling `record`, `write_commit`, or `parse_commit` with a title containing newline or carriage-return bytes, e.g. a multi-line commit message passed whole instead of just its first line.","commonSituations":"Passing a full commit message body as the title, or user input captured from a multi-line editor/textbox without normalizing newlines.","solutions":["Extract only the first line of the message (split on `\\n`) before passing it as the title","Replace or strip `\\r` and `\\n` characters from user-supplied input before recording","Validate the title is single-line in your own input handling and reject multi-line values with a clear message"],"exampleFix":"// before\nundo.record(repo, full_commit_message)?;\n// after\nlet title = full_commit_message.lines().next().unwrap_or_default();\nundo.record(repo, title)?;","handlingStrategy":"validation","validationCode":"fn is_single_line(title: &str) -> bool {\n    !title.as_bytes().iter().any(|b| *b == b'\\n' || *b == b'\\r')\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always take `message.lines().next()` as the title","Normalize CRLF input before passing titles","Reject multi-line input at the UI layer"],"tags":["validation","git","undo"],"backgroundTag":"invalid-argument-format","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"}