astrid-runtime/astrid · error
groups path has no parent directory
Error message
groups path has no parent directory
What it means
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.
Source
Thrown at crates/astrid-core/src/groups/io_impl.rs:77
}
custom.insert(name.clone(), group.clone());
}
let file = GroupsFileOwned { groups: custom };
let content = toml::to_string_pretty(&file).map_err(|e| {
GroupConfigError::Io(io::Error::other(format!(
"failed to serialize groups.toml: {e}"
)))
})?;
write_atomic(path, content.as_bytes())
}
}
#[cfg(unix)]
static TMP_COUNTER: AtomicU64 = AtomicU64::new(0);
fn write_atomic(path: &Path, data: &[u8]) -> GroupConfigResult<()> {
let parent = path.parent().ok_or_else(|| {
GroupConfigError::Io(io::Error::new(
io::ErrorKind::InvalidInput,
"groups path has no parent directory",
))
})?;
#[cfg(windows)]
crate::platform_fs::ensure_private_directory(parent)?;
#[cfg(not(windows))]
fs::create_dir_all(parent)?;
#[cfg(unix)]
{
use std::io::Write;
use std::os::unix::fs::OpenOptionsExt;
let seq = TMP_COUNTER.fetch_add(1, Ordering::Relaxed);
let tmp_path = path.with_extension(format!("toml.tmp.{}.{seq}", std::process::id()));
let mut f = fs::OpenOptions::new()
.write(true)View on GitHub (pinned to affd8760f4)
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
Example fix
// before
let path = PathBuf::from(""); // parent() == None
// after
let path = home.root().join("groups.json"); // parent == home root Defensive patterns
Strategy: validation
Validate before calling
fn saveable_groups_path(p: &std::path::Path) -> bool {
p.parent().is_some() && p.file_name().is_some()
}
assert!(saveable_groups_path(&groups_path), "groups path must name a file inside a directory"); Try / catch
match groups.save_to_path(&path) {
Err(GroupConfigError::Io(e)) if e.kind() == std::io::ErrorKind::InvalidInput => {
// path had no parent; reconstruct from a valid directory
},
other => other?,
} Prevention
- 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
When it happens
Trigger: 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().
Common situations: A configuration field holding the groups file path is empty or set to `/` due to a bad default, unexpanded variable, or truncated config value.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- live Astrid volume has no parent
- live Astrid volume has no name
- {} is not a real directory: {}
- {} overlaps the Astrid durable root: {} overlaps {}
- {VARIABLE} {detail}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/276a1e2050b0a1e7.
Report an issue: GitHub.