{"record":{"id":"1d17eb8564d91c48","repo":"GitoxideLabs/gitoxide","slug":"visit-non-tree-called-us","errorCode":null,"errorMessage":"visit_non_tree() called us","messagePattern":"visit_non_tree\\(\\) called us","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"gix-index/src/init.rs","lineNumber":154,"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            if self.invalid_path.is_none()\n                && let Err(err) = gix_validate::path::component(name, None, self.validate)\n            {\n                self.invalid_path = Some((self.path.clone(), err));\n            }\n        }\n\n        pub fn add_entry(&mut self, entry: &tree::EntryRef<'_>) {\n            let mode = match entry.mode.kind() {\n                EntryKind::Tree => unreachable!(\"visit_non_tree() called us\"),\n                EntryKind::Blob => Mode::FILE,\n                EntryKind::BlobExecutable => Mode::FILE_EXECUTABLE,\n                EntryKind::Link => Mode::SYMLINK,\n                EntryKind::Commit => Mode::COMMIT,\n            };\n            // There are leaf-names that require special validation, specific to their mode.\n            // Double-validate just for this case, as the previous validation didn't know the mode yet.\n            if self.invalid_path.is_none() {\n                let start = self.path.rfind_byte(b'/').map(|pos| pos + 1).unwrap_or_default();\n                if let Err(err) = gix_validate::path::component(\n                    self.path[start..].as_ref(),\n                    (entry.mode.kind() == EntryKind::Link).then_some(gix_validate::path::component::Mode::Symlink),\n                    self.validate,\n                ) {\n                    self.invalid_path = Some((self.path.clone(), err));\n                }\n            }\n","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-index/src/init.rs#L136-L172","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (custom Visit impl)\nfn visit_nontree(&mut self, entry: tree::EntryRef<'_>) -> Action {\n    self.add_entry(&entry);\n    Action::Continue\n}\n\n// after\nfn visit_nontree(&mut self, entry: tree::EntryRef<'_>) -> Action {\n    if entry.mode.kind() != EntryKind::Tree {\n        self.add_entry(&entry);\n    }\n    Action::Continue\n}","handlingStrategy":"type-guard","validationCode":"if entry.mode.kind() == EntryKind::Tree {\n    return Err(anyhow::anyhow!(\"tree entries must be routed to visit_tree, not add_entry\"));\n}","typeGuard":"fn is_non_tree(entry: &gix_traverse::tree::EntryRef<'_>) -> bool {\n    entry.mode.kind() != gix_object::tree::EntryKind::Tree\n}","tryCatchPattern":"// Panic-based invariant; guard at the call site:\nif is_non_tree(entry) { visitor.add_entry(entry); }","preventionTips":["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."],"tags":["panic","git","index","internal-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"}