astrid-runtime/astrid · error · io::Error
layout record has no parent
Error message
layout record has no parent
What it means
atomic_write derives the parent directory of the target path via Path::parent(); if the path has no parent component (e.g. a bare relative file name like "record.json"), it raises InvalidInput "layout record has no parent" instead of writing into an ambiguous location. The library requires an explicit directory for durable atomic layout-record writes (it must create the parent and fsync it).
Source
Thrown at crates/astrid-core/src/dirs_layout.rs:518
fn ensure_migration_capacity(_target: &Path, _source_bytes: u64) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"layout migration capacity probing is unavailable in a WebAssembly guest",
))
}
fn atomic_write(path: &Path, bytes: &[u8]) -> io::Result<()> {
#[cfg(windows)]
{
crate::platform_fs::atomic_write_private_file(path, bytes)
}
#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt as _;
let parent = path.parent().ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "layout record has no parent")
})?;
std::fs::create_dir_all(parent)?;
let name = path
.file_name()
.and_then(|name| name.to_str())
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"layout record has no file name",
)
})?;
let staged = parent.join(format!(".{name}.next"));
match std::fs::symlink_metadata(&staged) {
Ok(metadata) if metadata.file_type().is_file() => std::fs::remove_file(&staged)?,
Ok(_) => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("layout staging path is redirected: {}", staged.display()),View on GitHub (pinned to affd8760f4)
Solutions
- Pass an absolute or otherwise parent-bearing path, e.g. Path::new(".").join("version.json") or the layout directory joined with the file name.
- Verify the path with path.parent().is_some() before calling the API.
- Fix the caller/config that supplies the layout record path so it always includes the directory.
Example fix
// before
write_layout_version(Path::new("layout.json"), &record)?;
// after
let path = layout_dir.join("layout.json");
write_layout_version(&path, &record)?; Defensive patterns
Strategy: validation
Validate before calling
fn has_parent(path: &Path) -> bool { path.parent().is_some() }
if !has_parent(record_path) { return Err("record path needs a directory component"); } Type guard
fn writable_layout_path(path: &Path) -> Option<&Path> {
path.parent().map(|_| path)
} Try / catch
if let Err(e) = write_layout_version(path, record) {
if e.kind() == io::ErrorKind::InvalidInput && e.to_string().contains("no parent") {
eprintln!("supply a path with a directory component: {}", path.display());
}
} Prevention
- Always build record paths by joining the layout directory with a file name
- Reject bare relative file names in configuration
- Unit-test path builders to assert parent() is Some
When it happens
Trigger: Calling write_layout_version with a path such as Path::new("version.json") or any path whose parent() returns None (root-less relative name), so the ok_or_else branch fires in the unix block.
Common situations: Passing a bare file name from config instead of a full path; constructing the path by joining onto an empty base; tests or scripts that chdir and pass just a file name.
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
- layout record has no file name
- failed to persist {}: {e}
- layout staging path is redirected: {}
- durable layout records are unsupported on this operating sys
- private atomic-file backend is selected by Windows callers o
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/6d6be221813579a0.
Report an issue: GitHub.