astrid-runtime/astrid · error

invalid destination parent

Error message

invalid destination parent {destination_parent:?}: {error}

What it means

Raised by preflight_entry while walking destination parent directories: a non-empty destination_parent string fails FilesystemPath::new conversion. Ensures each ancestor of the destination is a valid logical path before creating it.

Solutions

  1. Inspect the receipt entry whose destination produces an invalid parent
  2. Fix the destination string so its parent segments are valid logical path components
  3. Regenerate the receipt if hand edits broke it
  4. Ensure paths use the logical '/' separator with relative components only

Example fix

// before: absolute destination yields bad parent
"destination": "/abs/path/file"
// after: relative logical destination
"destination": "abs/path/file"
Defensive patterns

Strategy: validation

Validate before calling

fn parent_is_valid(dest: &str) -> bool {
    let parent = dest.rsplit_once('/').map_or(String::new, |(p, _)| p.to_owned());
    parent.is_empty() || FilesystemPath::new(parent).is_ok()
}

Type guard

fn parse_parent(dest: &str) -> Option<FilesystemPath> {
    dest.rsplit_once('/').map(|(p, _)| p.to_string()).filter(|p| !p.is_empty()).and_then(|p| FilesystemPath::new(p).ok())
}

Try / catch

if let Err(e) = preflight_entry(...) {
    if e.kind() == io::ErrorKind::InvalidData {
        return Err(anyhow!("receipt destination structure invalid: {e}"));
    }
    return Err(e.into());
}

Prevention

When it happens

Trigger: Calling migrate_one_principal -> preflight_entry when a derived destination parent (computed by rsplit_once('/')) yields an invalid logical path, e.g. when the entry destination contains characters or structure FilesystemPath rejects.

Common situations: Receipts whose source/destination use Windows-style separators or absolute paths, edited receipts, or destinations mixing '/' conventions the logical path layer rejects.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/5f7a27f26b681502. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-kernel/src/principal_home_migration/mod.rs:647

            },
            Err(FilesystemError::NotFound(_)) => {},
            Err(error) => return Err(storage_error(&error)),
        },
    }

    // A missing destination ancestor is allowed only when its corresponding
    // source ancestor is a directory that this migration will publish. Walk
    // all the way to `home` so an existing file at a higher ancestor cannot be
    // hidden by a missing child lookup.
    let mut destination_parent = destination
        .as_str()
        .rsplit_once('/')
        .map_or_else(String::new, |(parent, _)| parent.to_owned());
    let source_path = source_root.join(&entry.source);
    let mut source_parent = source_path.parent().map(Path::to_path_buf);
    while !destination_parent.is_empty() {
        let parent_path = FilesystemPath::new(destination_parent.clone()).map_err(|error| {
            io::Error::new(
                io::ErrorKind::InvalidData,
                format!("invalid destination parent {destination_parent:?}: {error}"),
            )
        })?;
        match filesystem.stat(&parent_path) {
            Ok(existing) if existing.kind() == FilesystemEntryKind::Directory => {},
            Ok(_) => {
                return Err(conflict_fs(
                    &parent_path,
                    "destination parent is not a directory",
                ));
            },
            Err(FilesystemError::NotFound(_)) => {
                let source_parent_path = source_parent.as_deref().ok_or_else(|| {
                    conflict_fs(&parent_path, "destination parent has no source directory")
                })?;
                let metadata = fs::symlink_metadata(source_parent_path)?;
                if metadata.file_type().is_symlink() || !metadata.is_dir() {

View on GitHub (pinned to affd8760f4)