GitoxideLabs/gitoxide · error

changes can only be unstashed at the current HEAD

Error message

changes can only be unstashed at the current HEAD

What it means

Restoring saved worktree state is keyed by the commit the state was saved at; tix requires that commit to be the current HEAD. This error fires when `restore_manual` is given an `id` that differs from `repo.head_id()`, because applying the saved state at any other position could corrupt the correspondence between the stash and the tree it was captured against.

Solutions

  1. Move HEAD back to the commit the stash was saved at (checkout/reset), then unstash
  2. Verify `repo.head_id()` equals the stash's commit id before calling restore
  3. If the original commit is gone from history, recover it or manually apply the saved state from its reference

Example fix

// before
let stash_id = saved_commit_id;
stash::restore_manual(path, bare, stash_id)?;
// after
let repo = gix::open(path)?;
let head = repo.head_id().expect("born HEAD").detach();
assert_eq!(head, stash_id, "checkout the stash's commit first");
stash::restore_manual(path, bare, stash_id)?;
Defensive patterns

Strategy: validation

Validate before calling

let repo = gix::open(path)?;
let head = repo.head_id().context("born HEAD required")?.detach();
if head != stash_id {
    anyhow::bail!("checkout {} before unstashing", stash_id);
}

Prevention

When it happens

Trigger: Calling `stash::restore_manual` with an `id` not equal to the repository's current `head_id()` — e.g. after HEAD moved via checkout/reset/rebase between stashing and unstashing, or passing an arbitrary commit id.

Common situations: Trying to unstash after switching branches or rebasing; scripts holding a stale HEAD id; attempting to apply a stash recorded on another commit.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

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

        write!(notice, "; {warning}").expect("writing to a string cannot fail");
    }
    Ok(notice)
}

#[tracing::instrument(skip_all, fields(commit_id = %id))]
pub(crate) fn restore_manual(repository_path: &Path, bare: bool, id: ObjectId) -> Result<String> {
    let repo = open_repository(repository_path, bare, false).context("could not open repository to unstash changes")?;
    let workdir = repo
        .workdir()
        .context("unstashing changes requires a worktree")?
        .to_owned();
    if repo
        .head_id()
        .context("unstashing changes requires a born HEAD")?
        .detach()
        != id
    {
        anyhow::bail!("changes can only be unstashed at the current HEAD");
    }
    let name = reference(id)?;
    drop(repo);
    let saved = find(repository_path, bare, name)?.context("the selected commit has no saved worktree state")?;
    apply(repository_path, bare, &workdir, saved)
}

#[derive(Clone)]
pub(super) struct SavedStash {
    pub name: gix::refs::FullName,
    pub target: Target,
    pub warning: Option<String>,
}

#[tracing::instrument(skip_all, fields(stash = %name))]
pub(super) fn save(
    repository_path: &Path,
    bare: bool,

View on GitHub (pinned to e73179060b)