GitoxideLabs/gitoxide · error
continued refs have a target
Error message
continued refs have a target
What it means
This `expect` panic (gix-tix/src/edit/rebase.rs:200) occurs while rebuilding `ExpectedRef` entries for a rebase continuation. For each expected ref not present in `final_refs`, the code asserts that `expected.new` is `Some(target)`, i.e. every ref that is still being carried forward has a known new target. A `None` means the plan recorded a ref whose target is unknown, which would corrupt the ref-update plan.
Solutions
- Delete the stale rebase continuation state and restart the rebase from scratch.
- Filter refs with `new: None` out of the continuation instead of asserting (treat them as already resolved).
- Validate the continuation plan on load: reject or repair plans containing target-less expected refs before resuming.
Example fix
// before
target: expected.new.expect("continued refs have a target"),
// after
match expected.new {
Some(target) => target,
None => continue, // skip refs whose target was never recorded; they are already resolved
} Defensive patterns
Strategy: type-guard
Validate before calling
// Before resuming a continuation, validate the plan:
fn plan_is_resumable(plan: &Plan) -> bool {
plan.expected_refs.iter()
.filter(|e| !plan.final_refs.contains(&e.name))
.all(|e| e.new.is_some())
} Type guard
fn continuable(e: &ExpectedRef, final_refs: &HashSet<...>) -> Option<ExpectedRef> {
let target = e.new?;
(!final_refs.contains(&e.name)).then_some(ExpectedRef { target, ..e.clone() })
} Try / catch
// All panics here are process-fatal in Rust; guard by validating before resume:
if !plan_is_resumable(&plan) {
eprintln!("stale/incomplete rebase continuation; restart the rebase");
} Prevention
- Validate continuation state on load, before any ref-update planning.
- Skip or resolve refs with `new: None` instead of asserting.
- Bump/schema-check continuation files across gix-tix versions.
When it happens
Trigger: Running a rebase continuation (resume) where the saved plan's `expected_refs` contain an entry with `new: None` that is not in `final_refs`. This can happen if the continuation state was produced by an older/buggy version, was hand-edited, or if plan construction allowed pending refs without targets to be marked as continued.
Common situations: Resuming an interrupted rebase whose on-disk continuation file was written by a different gix-tix version (schema drift), or a crash mid-write leaving a truncated/incomplete continuation state.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- a stack always contains HEAD
- rebase worker panicked
- parser must have set some object value
- successful iteration has outcome
- valid ASCII
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/d6be9c1553dd51b1.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/rebase.rs:200
pub(crate) fn persist_objects(&mut self) -> Result<()> {
self.conflict.prepared.persist_objects()
}
pub(crate) fn map(&self, id: ObjectId) -> Option<ObjectId> {
self.rewritten.get(&id).copied().unwrap_or(Some(id))
}
pub(crate) fn continuation_plan(&self) -> Plan {
let expected_refs = self
.plan
.expected_refs
.iter()
.filter(|expected| !self.final_refs.contains(&expected.name))
.map(|expected| ExpectedRef {
name: expected.name.clone(),
old: expected.old,
target: expected.new.expect("continued refs have a target"),
new: expected.new,
follows_tip: expected.follows_tip,
editable: expected.editable,
placement: expected.placement.map(|target| match target {
PlanParent::Existing(id) => PlanParent::Existing(id),
PlanParent::Step(index) if index < self.continuation_start => {
PlanParent::Existing(self.produced[index])
}
PlanParent::Step(index) => PlanParent::Step(index - self.continuation_start),
}),
})
.collect();
let mut scope = self.produced[self.continuation_start..].to_vec();
scope.extend(self.remaining_squash.iter().flatten().copied());
let base = match self.plan.steps[self.continuation_start].parent {
PlanParent::Existing(id) => id,
PlanParent::Step(parent) => self.produced[parent],
};View on GitHub (pinned to e73179060b)