GitoxideLabs/gitoxide · error
could not retain ; original state was restored
Error message
could not retain {state_label}; original state was restored What it means
In gix-tix `stash save`, when persisting the new stash state fails, the code attempts a recovery via `git stash pop --index` to restore the user's original working-tree state. If the pop succeeds, the primary error is augmented with "could not retain {state_label}; original state was restored" — telling the user the operation failed but nothing was lost. If the pop also fails, a harsher message including git's stderr is used.
Solutions
- Verify your working tree is back to the original state (git status) before retrying
- Fix the root cause from the primary error (permissions, disk space) and retry the save
- If state was NOT restored (the alternate message), resolve manually with git stash list/pop
Defensive patterns
Strategy: try-catch
Validate before calling
// confirm the working tree is clean/restored before retrying a stash save
let status = Command::new("git").args(["-C", workdir, "status", "--porcelain"]).output()?;
let restored = String::from_utf8_lossy(&status.stdout).trim().is_empty(); Try / catch
match save_state(...) {
Err(e) if e.to_string().contains("original state was restored") => {
eprintln!("save failed but your changes are back in the worktree; safe to retry");
}
Err(e) => return Err(e),
Ok(v) => v,
} Prevention
- Check `git status` after any failed stash operation
- Keep disk space free so stash writes don't fail mid-operation
- Prefer library-native stash flows over shelling out to git when possible
When it happens
Trigger: Any failure of the stash-save operation (error variable `err`) during `save`/`save_manual` while a real worktree is checked out, followed by a successful `git stash pop --index` recovery.
Common situations: Disk/permission failures while creating the stash; conflicts preventing the restore path; environments where shelling out to `git` behaves differently than expected.
Understand the failure class
Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.
Related errors
- tix stash reference does not use a canonical full commit ID
- cannot drop stashed commit
- stashes at and would converge on
- rewritten commit already has saved worktree state
- changes can only be stashed at the current HEAD
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/fdc58d217cd653e2.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/stash.rs:245
.context("git stash push did not create refs/stash")?;
let id = stash.peel_to_id()?.detach();
if previous == Some(id) {
anyhow::bail!("git stash push did not create a new stash");
}
let target = Target::Object(id);
if let Err(err) = repo.edit_references([RefEdit::update(
name.clone(),
target.clone(),
PreviousValue::MustNotExist,
reflog_message,
)]) {
drop(repo);
let restore = Command::new("git")
.arg("-C")
.arg(workdir)
.args(["stash", "pop", "--index", "--quiet"])
.output();
return Err(anyhow::anyhow!(err)).context(match restore {
Ok(output) if output.status.success() => {
format!("could not retain {state_label}; original state was restored")
}
Ok(output) => format!(
"could not retain {state_label} and git stash pop failed: {}",
output.stderr.trim().to_str_lossy()
),
Err(restore) => {
format!("could not retain {state_label} and could not launch git stash pop: {restore}")
}
});
}
drop(repo);
let warning = match current(repository_path, bare)? {
Some(current) if current == id => {
let output = Command::new("git")
.arg("-C")View on GitHub (pinned to e73179060b)