{"record":{"id":"b15cb69c38627243","repo":"GitoxideLabs/gitoxide","slug":"every-parent-is-set-only-once","errorCode":null,"errorMessage":"every parent is set only once","messagePattern":"every parent is set only once","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitoxide-core/src/repository/tree.rs","lineNumber":78,"sourceCode":"\n        fn push_element(&mut self, name: &BStr) {\n            if name.is_empty() {\n                return;\n            }\n            if !self.path.is_empty() {\n                self.path.push(b'/');\n            }\n            self.path.push_str(name);\n        }\n    }\n\n    impl gix::traverse::tree::Visit for Traverse<'_, '_> {\n        fn pop_back_tracked_path_and_set_current(&mut self) {\n            self.path = self.path_deque.pop_back().unwrap_or_default();\n        }\n\n        fn pop_front_tracked_path_and_set_current(&mut self) {\n            self.path = self.path_deque.pop_front().expect(\"every parent is set only once\");\n        }\n\n        fn push_back_tracked_path_component(&mut self, component: &BStr) {\n            self.push_element(component);\n            self.path_deque.push_back(self.path.clone());\n        }\n\n        fn push_path_component(&mut self, component: &BStr) {\n            self.push_element(component);\n        }\n\n        fn pop_path_component(&mut self) {\n            self.pop_element();\n        }\n\n        fn visit_tree(&mut self, _entry: &EntryRef<'_>) -> Action {\n            self.stats.num_trees += 1;\n            std::ops::ControlFlow::Continue(true)","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gitoxide-core/src/repository/tree.rs#L60-L96","documentation":"The tree-traversal visitor in the CLI's tree listing maintains a deque mirroring the traversal's push/pop path protocol. `pop_front_tracked_path_and_set_current` expects a stored path to exist for every pop; the `.expect()` panics if the deque is empty, meaning the push/pop protocol was violated.","triggerScenarios":"A tree traversal calling `pop_front_tracked_path_and_set_current` more times than `push_back_tracked_path_component` was called — only possible via an internal gix traversal bug or a mismatched Visitor implementation.","commonSituations":"Using a custom `gix::traverse::tree::Visit` implementation that drops push events; gix version skew between traversal engine and visitor expectations.","solutions":["Ensure every push call has a matching pop by mirroring gix's default visitor logic.","Update gix to the latest version if the traversal engine changed its push/pop ordering.","Use `unwrap_or_default()` (as in the pop_back variant) as a defensive fallback."],"exampleFix":"// before\nself.path = self.path_deque.pop_front().expect(\"every parent is set only once\");\n\n// after\nself.path = self.path_deque.pop_front().unwrap_or_default();","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn pop_path(deque: &mut std::collections::VecDeque<gix::bstr::BString>) -> gix::bstr::BString {\n    deque.pop_front().unwrap_or_default()\n}","tryCatchPattern":"// guard the deque instead of expecting\nlet Some(path) = self.path_deque.pop_front() else {\n    self.path = Default::default();\n    return;\n};\nself.path = path;","preventionTips":["Mirror every traversal push event with exactly one pop in custom Visit impls","Copy the balanced push/pop logic from gix's built-in visitor","Test custom visitors against gix's own traversal tests"],"tags":["traversal","tree","panic","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-16T04:17:20.429Z"}