GitoxideLabs/gitoxide · error
an undo operation title must be one line
Error message
an undo operation title must be one line
What it means
`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.
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
Example fix
// before undo.record(repo, full_commit_message)?; // after let title = full_commit_message.lines().next().unwrap_or_default(); undo.record(repo, title)?;
Defensive patterns
Strategy: validation
Validate before calling
fn is_single_line(title: &str) -> bool {
!title.as_bytes().iter().any(|b| *b == b'\n' || *b == b'\r')
} Prevention
- Always take `message.lines().next()` as the title
- Normalize CRLF input before passing titles
- Reject multi-line input at the UI layer
When it happens
Trigger: 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.
Common situations: Passing a full commit message body as the title, or user input captured from a multi-line editor/textbox without normalizing newlines.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- undo metadata contains an unknown section
- the undo queue records itself
- undo reference sections are duplicated or out of order
- the undo queue is not a selectable revision
- an undo operation title cannot be empty
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/f2a825bc453a12a5.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/undo.rs:671
if let Some(hex) = value.strip_prefix(b"object:") {
let id = ObjectId::from_hex(hex).context("an undo object ID is invalid")?;
ensure!(
id.kind() == repo.object_hash(),
"an undo object ID uses the wrong hash kind"
);
return Ok(State::Object(id));
}
if let Some(name) = value.strip_prefix(b"symbolic:") {
return FullName::try_from(name.as_bstr())
.map(State::Symbolic)
.context("an undo symbolic target is invalid");
}
bail!("an undo reference state has an unknown encoding")
}
fn validate_title(title: &str) -> Result<()> {
ensure!(!title.is_empty(), "an undo operation title cannot be empty");
ensure!(
!title.as_bytes().iter().any(|byte| matches!(byte, b'\n' | b'\r')),
"an undo operation title must be one line"
);
Ok(())
}
fn retention_parents(repo: &gix::Repository, predecessor: ObjectId, changes: &[RefChange]) -> Result<Vec<ObjectId>> {
let mut parents = vec![predecessor];
let mut seen = HashSet::from([predecessor]);
for id in changes
.iter()
.flat_map(|change| [&change.before, &change.after])
.filter_map(|state| match state {
State::Object(id) => Some(*id),
State::Missing | State::Symbolic(_) => None,
})
{
if seen.contains(&id) {View on GitHub (pinned to e73179060b)