{"record":{"id":"5f7a27f26b681502","repo":"astrid-runtime/astrid","slug":"invalid-destination-parent-destination-parent","errorCode":null,"errorMessage":"invalid destination parent {destination_parent:?}: {error}","messagePattern":"invalid destination parent (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/principal_home_migration/mod.rs","lineNumber":647,"sourceCode":"            },\n            Err(FilesystemError::NotFound(_)) => {},\n            Err(error) => return Err(storage_error(&error)),\n        },\n    }\n\n    // A missing destination ancestor is allowed only when its corresponding\n    // source ancestor is a directory that this migration will publish. Walk\n    // all the way to `home` so an existing file at a higher ancestor cannot be\n    // hidden by a missing child lookup.\n    let mut destination_parent = destination\n        .as_str()\n        .rsplit_once('/')\n        .map_or_else(String::new, |(parent, _)| parent.to_owned());\n    let source_path = source_root.join(&entry.source);\n    let mut source_parent = source_path.parent().map(Path::to_path_buf);\n    while !destination_parent.is_empty() {\n        let parent_path = FilesystemPath::new(destination_parent.clone()).map_err(|error| {\n            io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\"invalid destination parent {destination_parent:?}: {error}\"),\n            )\n        })?;\n        match filesystem.stat(&parent_path) {\n            Ok(existing) if existing.kind() == FilesystemEntryKind::Directory => {},\n            Ok(_) => {\n                return Err(conflict_fs(\n                    &parent_path,\n                    \"destination parent is not a directory\",\n                ));\n            },\n            Err(FilesystemError::NotFound(_)) => {\n                let source_parent_path = source_parent.as_deref().ok_or_else(|| {\n                    conflict_fs(&parent_path, \"destination parent has no source directory\")\n                })?;\n                let metadata = fs::symlink_metadata(source_parent_path)?;\n                if metadata.file_type().is_symlink() || !metadata.is_dir() {","sourceCodeStart":629,"sourceCodeEnd":665,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/principal_home_migration/mod.rs#L629-L665","documentation":"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.","triggerScenarios":"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.","commonSituations":"Receipts whose source/destination use Windows-style separators or absolute paths, edited receipts, or destinations mixing '/' conventions the logical path layer rejects.","solutions":["Inspect the receipt entry whose destination produces an invalid parent","Fix the destination string so its parent segments are valid logical path components","Regenerate the receipt if hand edits broke it","Ensure paths use the logical '/' separator with relative components only"],"exampleFix":"// before: absolute destination yields bad parent\n\"destination\": \"/abs/path/file\"\n// after: relative logical destination\n\"destination\": \"abs/path/file\"","handlingStrategy":"validation","validationCode":"fn parent_is_valid(dest: &str) -> bool {\n    let parent = dest.rsplit_once('/').map_or(String::new, |(p, _)| p.to_owned());\n    parent.is_empty() || FilesystemPath::new(parent).is_ok()\n}","typeGuard":"fn parse_parent(dest: &str) -> Option<FilesystemPath> {\n    dest.rsplit_once('/').map(|(p, _)| p.to_string()).filter(|p| !p.is_empty()).and_then(|p| FilesystemPath::new(p).ok())\n}","tryCatchPattern":"if let Err(e) = preflight_entry(...) {\n    if e.kind() == io::ErrorKind::InvalidData {\n        return Err(anyhow!(\"receipt destination structure invalid: {e}\"));\n    }\n    return Err(e.into());\n}","preventionTips":["Keep destinations relative so derived parents are valid logical paths","Avoid Windows separators or absolute prefixes in receipt destinations","Validate both destination and its parent chain before migration"],"tags":["validation","paths","migration"],"backgroundTag":"invalid-url-format","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}