GitoxideLabs/gitoxide · error

every parent is set only once

Error message

every parent is set only once

What it means

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.

Solutions

  1. Ensure every push call has a matching pop by mirroring gix's default visitor logic.
  2. Update gix to the latest version if the traversal engine changed its push/pop ordering.
  3. Use `unwrap_or_default()` (as in the pop_back variant) as a defensive fallback.

Example fix

// before
self.path = self.path_deque.pop_front().expect("every parent is set only once");

// after
self.path = self.path_deque.pop_front().unwrap_or_default();
Defensive patterns

Strategy: type-guard

Type guard

fn pop_path(deque: &mut std::collections::VecDeque<gix::bstr::BString>) -> gix::bstr::BString {
    deque.pop_front().unwrap_or_default()
}

Try / catch

// guard the deque instead of expecting
let Some(path) = self.path_deque.pop_front() else {
    self.path = Default::default();
    return;
};
self.path = path;

Prevention

When it happens

Trigger: 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.

Common situations: Using a custom `gix::traverse::tree::Visit` implementation that drops push events; gix version skew between traversal engine and visitor expectations.

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


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/b15cb69c38627243. Report an issue: GitHub.

Appendix: source

Thrown at gitoxide-core/src/repository/tree.rs:78

        fn push_element(&mut self, name: &BStr) {
            if name.is_empty() {
                return;
            }
            if !self.path.is_empty() {
                self.path.push(b'/');
            }
            self.path.push_str(name);
        }
    }

    impl gix::traverse::tree::Visit for Traverse<'_, '_> {
        fn pop_back_tracked_path_and_set_current(&mut self) {
            self.path = self.path_deque.pop_back().unwrap_or_default();
        }

        fn pop_front_tracked_path_and_set_current(&mut self) {
            self.path = self.path_deque.pop_front().expect("every parent is set only once");
        }

        fn push_back_tracked_path_component(&mut self, component: &BStr) {
            self.push_element(component);
            self.path_deque.push_back(self.path.clone());
        }

        fn push_path_component(&mut self, component: &BStr) {
            self.push_element(component);
        }

        fn pop_path_component(&mut self) {
            self.pop_element();
        }

        fn visit_tree(&mut self, _entry: &EntryRef<'_>) -> Action {
            self.stats.num_trees += 1;
            std::ops::ControlFlow::Continue(true)

View on GitHub (pinned to e73179060b)