{"record":{"id":"b49aef4a6df6583f","repo":"GitoxideLabs/gitoxide","slug":"always-with-parent-directory","errorCode":null,"errorMessage":"always with parent directory","messagePattern":"always with parent directory","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"gix-ref/src/store/file/loose/reflog.rs","lineNumber":120,"sourceCode":"            name: &FullNameRef,\n            previous_oid: Option<ObjectId>,\n            new: &oid,\n            committer: Option<gix_actor::SignatureRef<'_>>,\n            message: &BStr,\n            mut force_create_reflog: bool,\n        ) -> Result<(), Error> {\n            let (reflog_base, full_name) = self.reflog_base_and_relative_path(name);\n            match self.write_reflog {\n                WriteReflog::Normal | WriteReflog::Always => {\n                    if self.write_reflog == WriteReflog::Always {\n                        force_create_reflog = true;\n                    }\n                    let mut options = std::fs::OpenOptions::new();\n                    options.append(true).read(false);\n                    let log_path = reflog_base.join(&full_name);\n\n                    if force_create_reflog || self.should_autocreate_reflog(&full_name) {\n                        let parent_dir = log_path.parent().expect(\"always with parent directory\");\n                        gix_tempfile::create_dir::all(parent_dir, Default::default()).map_err(|err| {\n                            Error::CreateLeadingDirectories {\n                                source: err,\n                                reflog_directory: parent_dir.to_owned(),\n                            }\n                        })?;\n                        options.create(true);\n                    }\n\n                    let file_for_appending = match options.open(&log_path) {\n                        Ok(f) => Some(f),\n                        Err(err) if err.kind() == std::io::ErrorKind::NotFound => None,\n                        Err(err) => {\n                            // TODO: when Kind::IsADirectory becomes stable, use that.\n                            if log_path.is_dir() {\n                                gix_tempfile::remove_dir::empty_depth_first(log_path.clone())\n                                    .and_then(|_| options.open(&log_path))\n                                    .map(Some)","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-ref/src/store/file/loose/reflog.rs#L102-L138","documentation":"When creating or appending to a reflog file, gix-ref derives the log path (`reflog_base.join(full_name)`) and asks for its parent directory to create leading directories. The `expect(\"always with parent directory\")` asserts that a joined path always has a parent component. A panic means the ref name was empty or degenerate so `log_path.parent()` returned `None`.","triggerScenarios":"Calling an API that writes a reflog (e.g. ref creation/update with reflog enabled, `reflog_create_or_append`) with a reference name that produces a log path with no parent, which should be impossible for validated non-empty ref names.","commonSituations":"Practically unreachable for users; if observed it points to a gix-ref bug or to code passing an empty/degenerate full ref name into reflog creation with `force_create_reflog` or autocreate configured (e.g. `core.logAllRefUpdates` matching).","solutions":["Verify the reference name being written is a fully qualified, non-empty name like `refs/heads/main`","Check `core.logAllRefUpdates` and any explicit reflog-creation flags to confirm which names trigger autocreate","If it happens with a valid `refs/...` name, file a gix-ref bug with the ref name and config","Work around by ensuring the reflog directory exists beforehand or by writing through a higher-level API that validates names"],"exampleFix":"// before\nlet name = \"\"; // degenerate name reaching reflog creation\nrepo.reference(name, target, true, log_message)?;\n// after\nlet name = \"refs/heads/main\";\nrepo.reference(name, target, true, log_message)?;","handlingStrategy":"validation","validationCode":"fn is_full_ref(name: &str) -> bool { name.starts_with(\"refs/\") && !name.is_empty() } // check before triggering reflog writes","typeGuard":null,"tryCatchPattern":"// Panic, not an error type; use catch_unwind at a boundary if you must attempt recovery\nstd::panic::catch_unwind(|| repo.reference(name, target, true, msg)).map(|r| r.ok())","preventionTips":["Always use fully qualified ref names (`refs/heads/...`) when writing refs with reflogs","Check `core.logAllRefUpdates` to know which refs get auto-created reflogs","Don't pass empty or relative ref names to ref-writing APIs","Report any reproduction as a gix-ref bug with the ref name"],"tags":["rust","panic","gix-ref","reflog","filesystem"],"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"}