{"record":{"id":"d6be9c1553dd51b1","repo":"GitoxideLabs/gitoxide","slug":"continued-refs-have-a-target","errorCode":null,"errorMessage":"continued refs have a target","messagePattern":"continued refs have a target","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/rebase.rs","lineNumber":200,"sourceCode":"\n    pub(crate) fn persist_objects(&mut self) -> Result<()> {\n        self.conflict.prepared.persist_objects()\n    }\n\n    pub(crate) fn map(&self, id: ObjectId) -> Option<ObjectId> {\n        self.rewritten.get(&id).copied().unwrap_or(Some(id))\n    }\n\n    pub(crate) fn continuation_plan(&self) -> Plan {\n        let expected_refs = self\n            .plan\n            .expected_refs\n            .iter()\n            .filter(|expected| !self.final_refs.contains(&expected.name))\n            .map(|expected| ExpectedRef {\n                name: expected.name.clone(),\n                old: expected.old,\n                target: expected.new.expect(\"continued refs have a target\"),\n                new: expected.new,\n                follows_tip: expected.follows_tip,\n                editable: expected.editable,\n                placement: expected.placement.map(|target| match target {\n                    PlanParent::Existing(id) => PlanParent::Existing(id),\n                    PlanParent::Step(index) if index < self.continuation_start => {\n                        PlanParent::Existing(self.produced[index])\n                    }\n                    PlanParent::Step(index) => PlanParent::Step(index - self.continuation_start),\n                }),\n            })\n            .collect();\n        let mut scope = self.produced[self.continuation_start..].to_vec();\n        scope.extend(self.remaining_squash.iter().flatten().copied());\n        let base = match self.plan.steps[self.continuation_start].parent {\n            PlanParent::Existing(id) => id,\n            PlanParent::Step(parent) => self.produced[parent],\n        };","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/rebase.rs#L182-L218","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ntarget: expected.new.expect(\"continued refs have a target\"),\n// after\nmatch expected.new {\n    Some(target) => target,\n    None => continue, // skip refs whose target was never recorded; they are already resolved\n}","handlingStrategy":"type-guard","validationCode":"// Before resuming a continuation, validate the plan:\nfn plan_is_resumable(plan: &Plan) -> bool {\n    plan.expected_refs.iter()\n        .filter(|e| !plan.final_refs.contains(&e.name))\n        .all(|e| e.new.is_some())\n}","typeGuard":"fn continuable(e: &ExpectedRef, final_refs: &HashSet<...>) -> Option<ExpectedRef> {\n    let target = e.new?;\n    (!final_refs.contains(&e.name)).then_some(ExpectedRef { target, ..e.clone() })\n}","tryCatchPattern":"// All panics here are process-fatal in Rust; guard by validating before resume:\nif !plan_is_resumable(&plan) {\n    eprintln!(\"stale/incomplete rebase continuation; restart the rebase\");\n}","preventionTips":["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."],"tags":["panic","rebase","continuation-state","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}