GitoxideLabs/gitoxide · error

exactly one time-travel destination is required

Error message

exactly one time-travel destination is required

What it means

`tix travel` accepts exactly one destination argument (absolute OID or relative spec like ^, ~, first, tip). Passing zero or multiple destinations makes the request ambiguous, so run() bails before performing any time travel.

Solutions

  1. Pass exactly one destination: an OID or a relative target (e.g. `^`, `~2`, `first`, `tip`)
  2. Quote variables in shell scripts so an empty value yields zero args deliberately and OIDs stay one argument
  3. Check `tix log`/`tix history` for the correct single target first

Example fix

// before
tix travel $A $B
// after
tix travel "$A"
Defensive patterns

Strategy: validation

Validate before calling

// Validate destination count before invoking
let dests: Vec<&str> = args.to.iter().collect();
if dests.len() != 1 {
    return Err(anyhow!("pass exactly one travel destination"));
}

Try / catch

if let Err(e) = run_travel(args) {
    if e.to_string().contains("exactly one time-travel destination") {
        eprintln!("usage: tix travel <oid|^|~|first|tip>");
    }
}

Prevention

When it happens

Trigger: Invoking `tix travel` with no `to` target, or with more than one destination such that the argument match falls through to the `_` arm in run().

Common situations: Copy-pasting multiple OIDs; script variable expanding empty or containing spaces producing two args; forgetting the destination entirely.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at gix-tix/src/command/travel.rs:67

pub(super) fn run(repository: gix::Repository, args: Args) -> Result<()> {
    let head = repository.head().context("could not read HEAD before time-travel")?;
    let head_id = head
        .id()
        .map(gix::Id::detach)
        .context("cannot time-travel from an unborn HEAD")?;
    let detached = head.is_detached();
    drop(head);
    let (selected, resolved_graph) = match (&args.revision, args.to) {
        (Some(revision), None) => super::resolve_commit(&repository, revision, "time-travel destination")?,
        (None, Some(to)) => {
            let hidden = crate::history::available_hidden_revisions(&repository, &[], true)?.0;
            let hidden_tips = crate::history::snapshot(&repository, &[], &hidden, false)?.hidden_tips;
            let graph = crate::edit::loaded_view_graph_with_hidden(&repository, &[], &hidden)?;
            let selected = relative_destination(&repository, &graph, &hidden_tips, head_id, to)?;
            (selected, Some(graph))
        }
        _ => anyhow::bail!("exactly one time-travel destination is required"),
    };
    if selected == head_id {
        println!("already at {}", crate::change_id::display(&repository, selected, 7)?);
        return Ok(());
    }

    let revisions = vec![OsString::from("HEAD"), OsString::from(selected.to_string())];
    let graph = match resolved_graph {
        Some(graph) => graph,
        None => crate::edit::loaded_view_graph_with(&repository, &revisions)?,
    };
    let forward = graph.is_ancestor(head_id, selected);
    if detached && !forward {
        let source_is_pinned = crate::history::all_pins(&repository)?
            .into_iter()
            .any(|pin| graph.is_ancestor(head_id, pin.id));
        if !source_is_pinned {
            anyhow::bail!(

View on GitHub (pinned to e73179060b)