gitbutlerapp/gitbutler · error
Base commit must exist if provided
Error message
Base commit must exist if provided: {base} What it means
Rebase::new accepts an optional base commit that the rebase builds on top of. If a base ObjectId is supplied, the library verifies the object exists in the repository before constructing the builder; otherwise later steps would fail confusingly. This fail-fast check reports an unknown base immediately.
Solutions
- Fetch the missing commit (git fetch origin <sha>) so it exists locally.
- Verify the base id with `git cat-file -e <oid>` before calling Rebase::new.
- Pass None as the base if no base commit is required, or correct the stale id.
Example fix
// before Rebase::new(&repo, Some(stale_oid), None, steps)?; // after assert!(repo.has_object(stale_oid), "base must exist"); Rebase::new(&repo, Some(stale_oid), None, steps)?;
Defensive patterns
Strategy: validation
Validate before calling
if let Some(base) = base {
if !repo.has_object(base) {
return Err(anyhow!("base commit {} missing locally; fetch first", base));
}
} Try / catch
match rebase_result {
Err(e) if e.to_string().starts_with("Base commit must exist") => {
// fetch the missing oid then retry
}
other => other?,
} Prevention
- Verify base ids with `git cat-file -e` before constructing a Rebase
- Fetch commits referenced from other clones/remotes
- Refresh base ids after upstream force-pushes
When it happens
Trigger: Calling Rebase::new(repo, base, base_substitute, ...) with Some(oid) where repo.has_object(oid) is false — a commit id not present in the local object database.
Common situations: Passing a commit id from another clone/remote without fetching it; typos or truncated SHAs; referencing a commit that was garbage-collected; stale workspace state after force-push.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Cannot merge : merging into / resulted in conflicts. Rebase…
- Cherry picks can only be done for single-parent commits…
- Encountered conflict when merging tree
- Expected target tip selector to point to a pick
- Failed to find commit
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/c54590807f07e6c3.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-rebase/src/lib.rs:101
/// that all other commits should be placed on top of.
/// If `None` this means the first picked commit will have no parents.
/// This means that the first [picked commit](Self::steps()) will be placed right on top of `base`.
///
/// If the first pick refers to a merge-commit then we will have to prove it's connected to the `base` commit.
/// If that `base`, however, is also a new commit, we'd have no way of figuring out which parent in the picked merge
/// is replaced with `base` to know which commits are involved in the merge.
/// The `base_substitute` passed here is the commit that stands in for `base` in the original graph that the
/// picked merge commit is linked to.
pub fn new(
repo: &'repo gix::Repository,
base: impl Into<Option<gix::ObjectId>>,
base_substitute: Option<gix::ObjectId>,
) -> Result<Self> {
let base = base.into();
if let Some(base) = base
&& !repo.has_object(base)
{
bail!("Base commit must exist if provided: {base}");
}
Ok(Self {
repo,
base,
base_substitute,
steps: Vec::new(),
rebase_noops: true, // default to always rebasing
})
}
/// Adds and validates a list of rebase steps.
/// Ordered oldest (parentmost) to newest (childmost). Reference steps refer to the commit that precedes them.
/// Note that `steps` will extend whatever steps were added before.
pub fn steps(&mut self, steps: impl IntoIterator<Item = RebaseStep>) -> Result<&mut Self> {
for step in steps {
self.validate_step(&step)?;
self.steps.push(step);
}View on GitHub (pinned to 58e5313667)