GitoxideLabs/gitoxide · error · anyhow::Error

could not persist a prepared rebase object

Error message

could not persist a prepared rebase object: {err}

What it means

During a tix rebase, prepared objects are staged in memory and then flushed with `repo.write_buf_with_known_id`. If any write fails, the error is wrapped as "could not persist a prepared rebase object: {err}" and the rebase cannot continue. `write_buf_with_known_id` also validates the caller-supplied id, so a hash mismatch surfaces here.

Solutions

  1. Free disk space / fix .git/objects permissions and retry the rebase
  2. Check the inner error for which object failed; verify it isn't an id mismatch
  3. Abort and restart the rebase; in-memory objects are rebuilt deterministically
Defensive patterns

Strategy: try-catch

Validate before calling

fn odb_writable(repo: &gix::Repository) -> bool {
    repo.workdir()
        .map(|d| d.join("objects").metadata().map(|m| !m.permissions().readonly()).unwrap_or(false))
        .unwrap_or(false)
}

Try / catch

if let Err(e) = rebase.persist_objects() {
    anyhow::bail!("rebase aborted, prepared objects not persisted: {e:#}");
}

Prevention

When it happens

Trigger: Rebase edit operations in gix-tix (`edit/rebase.rs` persist path) where the object database write fails — unwritable .git/objects, disk full, or object id/kind mismatch between computed and stored data.

Common situations: Disk-quota or permission problems mid-rebase; very large rebases exhausting space; mismatches after hash-algorithm or library version changes.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

Thrown at gix-tix/src/edit/rebase.rs:2158

    let selected = prepared.selected.context("an enriched edit must select its commit")?;
    let Some((object, data, enrichment)) = crate::enrich::prepare_headers(&prepared.repo, selected, headers)? else {
        return Ok(None);
    };
    prepared.enrichment = Some((object, data));
    Ok(Some(enrichment))
}

impl Prepared {
    fn persist_objects(&mut self) -> Result<()> {
        let objects = self
            .repo
            .objects
            .take_object_memory()
            .context("candidate object memory was unavailable")?;
        for (id, (kind, data)) in objects.iter() {
            self.repo
                .write_buf_with_known_id(*kind, data, *id)
                .map_err(|err| anyhow::anyhow!("could not persist a prepared rebase object: {err}"))?;
        }
        self.repo.objects.set_object_memory(Default::default());
        Ok(())
    }

    fn finish(&mut self) -> Result<Outcome> {
        let mut resource_edits = super::stash::rewrite_edits(&self.repo, &self.stash_rewritten, &self.removed)?;
        let note_edits = note_rewrite_edits(&self.repo, &self.note_rewrites, &self.committer)?;
        let enrichment_edits = self
            .enrichment
            .take()
            .map(|(object, data)| enrichment_edits(&self.repo, object, data, &self.committer))
            .transpose()?
            .unwrap_or_else(|| super::stash::RewriteEdits {
                forward: Vec::new(),
                rollback: Vec::new(),
            });
        resource_edits.forward.extend(note_edits.forward);

View on GitHub (pinned to e73179060b)