GitoxideLabs/gitoxide · error

already has saved worktree state

Error message

{} already has saved worktree state

What it means

A tix stash reference is named after the commit at which state was saved, so at most one saved worktree state can exist per commit. This error fires when `save_manual` finds an existing reference under that commit's stash name, meaning a stash already exists for this commit and creating another would overwrite it. It reports the short hash of the commit.

Solutions

  1. Restore (`unstash`) the existing saved state first, then make the new stash
  2. Delete the existing stash reference if its saved state is no longer needed
  3. Check for an existing stash (e.g. via `find`/`try_find_reference` with `reference(id)`) before saving

Example fix

// before blind re-stash
stash::save_manual(path, bare, workdir, head_id)?;
// after: clear existing stash for this commit first
let name = tix::edit::stash::reference(head_id)?;
if repo.try_find_reference(name.as_ref())?.is_some() {
    tix::edit::stash::restore_manual(path, bare, head_id)?;
}
stash::save_manual(path, bare, workdir, head_id)?;
Defensive patterns

Strategy: validation

Validate before calling

let name = tix::edit::stash::reference(id)?;
let repo = gix::open(path)?;
let exists = repo.try_find_reference(name.as_ref())?.is_some();
if exists {
    anyhow::bail!("a stash already exists for {}; restore or delete it first", id);
}

Type guard

fn has_saved_state(repo: &gix::Repository, id: ObjectId) -> bool {
    let name = tix::edit::stash::reference(id).expect("valid id");
    repo.try_find_reference(name.as_ref()).map(|r| r.is_some()).unwrap_or(false)
}

Prevention

When it happens

Trigger: Calling `stash::save_manual` twice at the same HEAD commit without restoring/deleting the first stash; restoring a repo where an earlier tix stash session left a reference for the current HEAD.

Common situations: Repeated `tix stash` at the same commit; forgetting a previous stash after a failed restore; re-running a script that stashes idempotently.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at gix-tix/src/edit/stash.rs:143

    let head = repo
        .head_id()
        .context("stashing changes requires a born HEAD")?
        .detach();
    if head != id {
        anyhow::bail!("changes can only be stashed at the current HEAD");
    }
    if repo
        .index_or_empty()
        .context("could not inspect the index before stashing")?
        .entries()
        .iter()
        .any(|entry| entry.stage() != gix::index::entry::Stage::Unconflicted)
    {
        anyhow::bail!("cannot stash changes with unresolved index conflicts");
    }
    let name = reference(id)?;
    if repo.try_find_reference(name.as_ref())?.is_some() {
        anyhow::bail!("{} already has saved worktree state", id.to_hex_with_len(7));
    }
    drop(repo);
    if !super::review::is_dirty(&workdir)? {
        anyhow::bail!("there are no worktree or index changes to stash");
    }
    let saved = save(
        repository_path,
        bare,
        &workdir,
        name,
        format!("tix {}", id.to_hex_with_len(7)),
        "tix commit stash",
        "commit state",
    )?;
    let mut notice = format!("stashed changes at {}", id.to_hex_with_len(7));
    if let Some(warning) = saved.warning {
        write!(notice, "; {warning}").expect("writing to a string cannot fail");
    }

View on GitHub (pinned to e73179060b)