gitbutlerapp/gitbutler · error · anyhow::Error

CLI id '{short_id}' is ambiguous for {kind} in IdMap

Error message

CLI id '{short_id}' is ambiguous for {kind} in IdMap

What it means

Thrown by `lookup_cli_id_for_short_id`: after filtering IdMap matches by exact short-string equality and the kind predicate, more than one entry survives. The short id is too short to name exactly one entity of that kind, so the command refuses to guess.

Source

Thrown at crates/but/src/command/legacy/status/mod.rs:1855

    Ok(())
}

fn lookup_cli_id_for_short_id(
    id_map: &IdMap,
    repo: &gix::Repository,
    short_id: &str,
    predicate: impl Fn(&CliId) -> bool,
    kind: &str,
) -> anyhow::Result<CliId> {
    let mut matches = id_map.parse_using_repo(short_id, repo)?;
    matches.retain(|id| id.to_short_string() == short_id && predicate(id));

    match matches.len() {
        1 => Ok(matches.remove(0)),
        0 => Err(anyhow::anyhow!(
            "Could not find {kind} CLI id '{short_id}' in IdMap"
        )),
        _ => Err(anyhow::anyhow!(
            "CLI id '{short_id}' is ambiguous for {kind} in IdMap"
        )),
    }
}

fn status_letter(status: &TreeStatus) -> char {
    match status {
        TreeStatus::Addition { .. } => 'A',
        TreeStatus::Deletion { .. } => 'D',
        TreeStatus::Modification { .. } => 'M',
        TreeStatus::Rename { .. } => 'R',
    }
}

pub fn status_letter_ui(status: &ui::TreeStatus) -> char {
    match status {
        ui::TreeStatus::Addition { .. } => 'A',
        ui::TreeStatus::Deletion { .. } => 'D',

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Use more characters of the id to disambiguate — extend the prefix until it is unique
  2. List the current ids (`but status` / the relevant list command) to see which entries collide and pick the right one
  3. In scripts, prefer full-length git shas over short CLI ids to avoid collisions entirely

Example fix

# before: 'c3' matches two entries
but <cmd> c3

# after: longer prefix
but <cmd> c31
Defensive patterns

Strategy: validation

Validate before calling

let hits: Vec<_> = id_map
    .parse_using_repo(short_id, repo)?
    .into_iter()
    .filter(|id| id.to_short_string() == short_id && kind_predicate(id))
    .collect();
if hits.len() > 1 {
    anyhow::bail!("'{short_id}' matches {} {kind} ids; extend the prefix", hits.len());
}

Try / catch

match lookup_cli_id_for_short_id(id_map, repo, short_id, |i| is_commit(i), "commit") {
    Ok(cli_id) => { /* use */ }
    Err(err) if err.to_string().contains("ambiguous") => {
        // lengthen the prefix by one or more characters and retry
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Two commits (or branches) share the same short id string in the IdMap — common right after history-rewriting operations (amend, rebase, ship) that create parallel entries — and the user passes that id to a command resolving a single entity.

Common situations: Short ids after amend/rebase when old and new entries coexist; large workspaces where the short-id space for that kind collided; scripts using a fixed prefix length that is too short for the workspace's size.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/806e87c53e9d1523. Report an issue: GitHub.