GitoxideLabs/gitoxide · error

must be written as Name

Error message

{field} must be written as Name <email>

What it means

Identity headers (Author/Committer) in the edit document must be exactly `Name <email>` — no empty name/email and no trailing time component, because the time is supplied separately as `gix::date::Time`. `actor` parses the value with `gix::actor::SignatureRef::from_bytes` and bails with this error when name or email is empty or the parsed time field is non-empty.

Solutions

  1. Write the identity as `Name <email>` with both parts non-empty and no timestamp.
  2. Strip the time component if you copied a raw commit signature line.
  3. Fill in the author name/email in your git config or pass a complete identity programmatically.
  4. If `from_bytes` itself fails, the underlying 'could not parse {field} identity' error points at the malformed bytes.

Example fix

// before (document header)
Author:  <dev@example.com> 1700000000 +0100
// after
Author: Jane Dev <dev@example.com>
Defensive patterns

Strategy: validation

Validate before calling

fn identity_well_formed(value: &[u8]) -> bool {
    gix::actor::SignatureRef::from_bytes(value)
        .ok()
        .and_then(|s| s.trim().ok())
        .map_or(false, |s| !s.name.is_empty() && !s.email.is_empty() && s.time.is_empty())
}

Try / catch

match document_with_author(repo, old_id, author_value, time) {
    Ok(doc) => doc,
    Err(e) if e.to_string().contains("must be written as Name <email>") => {
        eprintln!("Author must look like: Jane Dev <jane@example.com> (no timestamp).");
        Err(e)
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `actor` (via `document_with_author`, `apply_conflict_reporting`, or `apply_message_reporting`) with a `value` like `""`, `"<a@b>"`, `"Name"`, `"Name <>"`, or `"Name <a@b> 1700000000 +0100"` — i.e. anything whose parsed name/email is empty or that carries an embedded timestamp.

Common situations: Copying a raw git signature line (which includes the timestamp) into the document; leaving the author line blank; forgetting the `<email>` part; scripted header generation writing incomplete identities.

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


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/d7146d2c42052eb0. Report an issue: GitHub.

Appendix: source

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

}

fn trim_cr(line: &[u8]) -> &[u8] {
    line.strip_suffix(b"\r").unwrap_or(line)
}

fn date(value: &[u8], field: &str) -> Result<gix::date::Time> {
    let value = std::str::from_utf8(value).with_context(|| format!("{field} date is not UTF-8"))?;
    gix::date::parse(value, None)
        .map_err(|err| anyhow::Error::new(err.into_error()))
        .with_context(|| format!("could not parse {field} date"))
}

pub(super) fn actor(value: &[u8], time: gix::date::Time, field: &str) -> Result<gix::actor::Signature> {
    let parsed = gix::actor::SignatureRef::from_bytes(value)
        .with_context(|| format!("could not parse {field} identity"))?
        .trim();
    if parsed.name.is_empty() || parsed.email.is_empty() || !parsed.time.is_empty() {
        anyhow::bail!("{field} must be written as Name <email>");
    }
    Ok(gix::actor::Signature {
        name: parsed.name.into(),
        email: parsed.email.into(),
        time,
    })
}

#[cfg(test)]
mod tests {
    use std::process::Command;

    use super::*;

    #[test]
    fn parses_the_edit_document() -> gix_testtools::Result {
        let input = b"Author: A U Thor <author@example.com>\n\
                      AuthorDate: 2026-08-12 10:20:30 +0200\n\

View on GitHub (pinned to e73179060b)