{"record":{"id":"45b3f4a8fe8a9d64","repo":"GitoxideLabs/gitoxide","slug":"we-exit-as-soon-as-everything-is-consumed","errorCode":null,"errorMessage":"we exit as soon as everything is consumed","messagePattern":"we exit as soon as everything is consumed","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-object/src/tree/editor.rs","lineNumber":266,"sourceCode":"                                WriteMode::FromCursor => {}\n                            }\n                            self.trees.insert(rela_path, tree);\n                            return Ok(root_tree_id);\n                        }\n                        Err(err) => {\n                            self.trees.insert(rela_path, tree);\n                            return Err(err);\n                        }\n                    }\n                } else if !tree.entries.is_empty() {\n                    out(&tree)?;\n                }\n            } else {\n                parents.push((parent_idx, rela_path, tree));\n            }\n        }\n\n        unreachable!(\"we exit as soon as everything is consumed\")\n    }\n\n    fn upsert_or_remove_at_pathbuf<I, C>(&mut self, rela_path: I, edit: EditMode) -> Result<&mut Self, Error>\n    where\n        I: IntoIterator<Item = C>,\n        C: AsRef<BStr>,\n    {\n        let mut path_buf = self.path_buf.borrow_mut();\n        let mut cursor = self.trees.get_mut(path_buf.as_bstr()).expect(\"root is always present\");\n        let mut rela_path = rela_path.into_iter().peekable();\n        let new_kind_is_tree = matches!(edit, EditMode::Upsert(EntryKind::Tree, _, _));\n        while let Some(name) = rela_path.next() {\n            let name = name.as_ref();\n            if name.is_empty() {\n                return Err(Error::EmptyPathComponent);\n            }\n            let is_last = rela_path.peek().is_none();\n            let mut needs_sorting = false;","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-object/src/tree/editor.rs#L248-L284","documentation":"`write_at_pathbuf` in `gix-object`'s `TreeEditor` loops consuming pending parent entries, and each iteration either finishes (returns Ok) or re-queues parents. The trailing `unreachable!` asserts the loop always exits. Firing means the pending-queue reduction logic failed to converge, an internal editor invariant break.","triggerScenarios":"Only via a bug in `TreeEditor::write`/`upsert` bookkeeping (e.g. a cycle or un-reduced parent entry), not via user-provided paths; users just call `editor.upsert(...)` then `editor.write(...)`.","commonSituations":"Should never occur; if it does, it happens while writing deeply nested or conflicting path edits to a tree.","solutions":["Report upstream with the sequence of `upsert`/`remove` edits and the base tree that reproduce it.","Update `gix-object` to the latest version.","If maintaining, add a bounded-iteration guard returning an error instead of panicking."],"exampleFix":"// before\nunreachable!(\"we exit as soon as everything is consumed\")\n// after\nErr(message(\"tree editor write did not converge; internal error\"))","handlingStrategy":"try-catch","validationCode":"// Validate edited paths are unique and well-formed before writing\n// Use BString paths without duplicate entries; check for conflicting upserts on the same path","typeGuard":null,"tryCatchPattern":"let result = std::panic::catch_unwind(|| editor.write(buf));\n// treat panic as a bug: file an issue with the edit sequence","preventionTips":["Avoid applying the same editor to writes after errors without resetting","Cover deep-nesting edits in tests","Update gix-object promptly on releases"],"tags":["rust","panic","internal-invariant","tree-editor"],"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-16T04:17:20.429Z"}