GitoxideLabs/gitoxide · warning
visit_non_tree() called us
Error message
visit_non_tree() called us
What it means
This is a caller-contract assertion inside the index-writing tree visitor: `add_entry()` only ever expects to be called with non-tree entries, because tree entries are routed to the tree-recursion callback (`visit_tree`) by the `gix_traverse::tree` visitor trait. The library panics if a `tree::EntryRef` with `EntryKind::Tree` reaches `add_entry`, which can only happen if the visitor dispatch contract is broken.
Solutions
- If you wrote a custom `gix_traverse::tree::Visit`, route `EntryKind::Tree` entries to the `visit_tree` callback, not `visit_nontree`/`add_entry`.
- Update or bisect gix-index/gix-traverse versions; this indicates an internal bug worth reporting upstream.
- As a workaround, filter entries by mode kind before handing them to the visitor pipeline.
Example fix
// before (custom Visit impl)
fn visit_nontree(&mut self, entry: tree::EntryRef<'_>) -> Action {
self.add_entry(&entry);
Action::Continue
}
// after
fn visit_nontree(&mut self, entry: tree::EntryRef<'_>) -> Action {
if entry.mode.kind() != EntryKind::Tree {
self.add_entry(&entry);
}
Action::Continue
} Defensive patterns
Strategy: type-guard
Validate before calling
if entry.mode.kind() == EntryKind::Tree {
return Err(anyhow::anyhow!("tree entries must be routed to visit_tree, not add_entry"));
} Type guard
fn is_non_tree(entry: &gix_traverse::tree::EntryRef<'_>) -> bool {
entry.mode.kind() != gix_object::tree::EntryKind::Tree
} Try / catch
// Panic-based invariant; guard at the call site:
if is_non_tree(entry) { visitor.add_entry(entry); } Prevention
- Implement `gix_traverse::tree::Visit` callbacks so tree entries go to `visit_tree` and others to `visit_nontree`.
- Never forward all entries to a single callback in custom visitors.
- Keep gix-index/gix-traverse versions in sync within the workspace.
When it happens
Trigger: Only via a bug in the traversal wiring: the tree visitor implementation calls `add_entry` for an entry whose mode kind is `Tree` instead of routing it through the tree callback. End users of `gix_index` cannot trigger it directly through public APIs.
Common situations: Encountered during internal refactors of the tree-visit/index-write path, or when embedding a custom `gix_traverse::tree::Visit` implementation that forwards all entries to the non-tree callback.
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
- invalid mode change: can't flip executable bit of
- BUG: ResolvedSignatures don't exist here when nothing is set
- initial hunks are never ancestors
- find returned a cached commit, so we expect cache to be…
- at least one chunk
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/1d17eb8564d91c48.
Report an issue: GitHub.
Appendix: source
Thrown at gix-index/src/init.rs:154
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);
if self.invalid_path.is_none()
&& let Err(err) = gix_validate::path::component(name, None, self.validate)
{
self.invalid_path = Some((self.path.clone(), err));
}
}
pub fn add_entry(&mut self, entry: &tree::EntryRef<'_>) {
let mode = match entry.mode.kind() {
EntryKind::Tree => unreachable!("visit_non_tree() called us"),
EntryKind::Blob => Mode::FILE,
EntryKind::BlobExecutable => Mode::FILE_EXECUTABLE,
EntryKind::Link => Mode::SYMLINK,
EntryKind::Commit => Mode::COMMIT,
};
// There are leaf-names that require special validation, specific to their mode.
// Double-validate just for this case, as the previous validation didn't know the mode yet.
if self.invalid_path.is_none() {
let start = self.path.rfind_byte(b'/').map(|pos| pos + 1).unwrap_or_default();
if let Err(err) = gix_validate::path::component(
self.path[start..].as_ref(),
(entry.mode.kind() == EntryKind::Link).then_some(gix_validate::path::component::Mode::Symlink),
self.validate,
) {
self.invalid_path = Some((self.path.clone(), err));
}
}
View on GitHub (pinned to e73179060b)