{"record":{"id":"276a1e2050b0a1e7","repo":"astrid-runtime/astrid","slug":"groups-path-has-no-parent-directory","errorCode":null,"errorMessage":"groups path has no parent directory","messagePattern":"groups path has no parent directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/groups/io_impl.rs","lineNumber":77,"sourceCode":"            }\n            custom.insert(name.clone(), group.clone());\n        }\n        let file = GroupsFileOwned { groups: custom };\n        let content = toml::to_string_pretty(&file).map_err(|e| {\n            GroupConfigError::Io(io::Error::other(format!(\n                \"failed to serialize groups.toml: {e}\"\n            )))\n        })?;\n        write_atomic(path, content.as_bytes())\n    }\n}\n\n#[cfg(unix)]\nstatic TMP_COUNTER: AtomicU64 = AtomicU64::new(0);\n\nfn write_atomic(path: &Path, data: &[u8]) -> GroupConfigResult<()> {\n    let parent = path.parent().ok_or_else(|| {\n        GroupConfigError::Io(io::Error::new(\n            io::ErrorKind::InvalidInput,\n            \"groups path has no parent directory\",\n        ))\n    })?;\n    #[cfg(windows)]\n    crate::platform_fs::ensure_private_directory(parent)?;\n    #[cfg(not(windows))]\n    fs::create_dir_all(parent)?;\n\n    #[cfg(unix)]\n    {\n        use std::io::Write;\n        use std::os::unix::fs::OpenOptionsExt;\n\n        let seq = TMP_COUNTER.fetch_add(1, Ordering::Relaxed);\n        let tmp_path = path.with_extension(format!(\"toml.tmp.{}.{seq}\", std::process::id()));\n        let mut f = fs::OpenOptions::new()\n            .write(true)","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/groups/io_impl.rs#L59-L95","documentation":"write_atomic in the groups config module needs the parent directory of the target path to place its temporary file before renaming; if path.parent() returns None there is no parent to write into. Astrid raises GroupConfigError::Io with io::ErrorKind::InvalidInput for this case. In practice this only happens when the path has no parent component, i.e. it is a bare root like `/` or an empty/degenerate path.","triggerScenarios":"Calling save_to_path (groups config persistence) with a Path that is the filesystem root `/`, an empty path, or otherwise yields None from Path::parent().","commonSituations":"A configuration field holding the groups file path is empty or set to `/` due to a bad default, unexpanded variable, or truncated config value.","solutions":["Fix the groups path so it names a file inside a real directory, e.g. ~/.astrid/groups.json instead of an empty or root path","Check the config source that supplies the path for empty/unexpanded values and validate it before save","If constructing the path programmatically, join a filename onto a directory (dir.join(\"groups.json\")) so parent() is always Some"],"exampleFix":"// before\nlet path = PathBuf::from(\"\");            // parent() == None\n// after\nlet path = home.root().join(\"groups.json\"); // parent == home root","handlingStrategy":"validation","validationCode":"fn saveable_groups_path(p: &std::path::Path) -> bool {\n    p.parent().is_some() && p.file_name().is_some()\n}\nassert!(saveable_groups_path(&groups_path), \"groups path must name a file inside a directory\");","typeGuard":null,"tryCatchPattern":"match groups.save_to_path(&path) {\n    Err(GroupConfigError::Io(e)) if e.kind() == std::io::ErrorKind::InvalidInput => {\n        // path had no parent; reconstruct from a valid directory\n    },\n    other => other?,\n}","preventionTips":["Never configure an empty or root ('/') groups path; always a filename under a directory","Build paths with dir.join(\"groups.json\") instead of raw strings from config","Validate configured paths at startup, before any save"],"tags":["io","filesystem","path-validation","configuration"],"backgroundTag":"invalid-argument-value","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}