{"record":{"id":"d3048f2f55d9007c","repo":"astrid-runtime/astrid","slug":"layout-migration-source-is-redirected-or-not-a-dir","errorCode":null,"errorMessage":"layout migration source is redirected or not a directory: {}","messagePattern":"layout migration source is redirected or not a directory: (.+?)","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/dirs_layout_records.rs","lineNumber":267,"sourceCode":"        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            \"layout destination path cannot be represented losslessly on this platform\",\n        ));\n    }\n    Ok(path)\n}\n\npub(super) fn inventory_tree(path: &Path) -> io::Result<LayoutTreeIdentityV1> {\n    let mut hasher = blake3::Hasher::new_derive_key(\"astrid layout source inventory v1\");\n    let mut entries = 0_u64;\n    let mut bytes = 0_u64;\n    match std::fs::symlink_metadata(path) {\n        Err(error) if error.kind() == io::ErrorKind::NotFound => {\n            hasher.update(b\"absent\");\n        },\n        Err(error) => return Err(error),\n        Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_dir() => {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"layout migration source is redirected or not a directory: {}\",\n                    path.display()\n                ),\n            ));\n        },\n        Ok(_) => {\n            crate::platform_fs::verify_no_redirects(path)?;\n            inventory_directory(path, path, &mut hasher, &mut entries, &mut bytes)?;\n        },\n    }\n    Ok(LayoutTreeIdentityV1 {\n        path_encoding: \"os-str-encoded-bytes-v1\".to_owned(),\n        physical_path_hex: physical_path_hex(path)?,\n        inventory_algorithm: \"blake3-derive-key-v1\".to_owned(),\n        inventory_digest: hasher.finalize().to_hex().to_string(),\n        entries,","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/dirs_layout_records.rs#L249-L285","documentation":"inventory_tree inventories a migration source directory to fingerprint it into a LayoutTreeIdentityV1. If symlink_metadata on the source succeeds but the entry is a symlink or not a directory, the function refuses with this InvalidData error rather than hashing a redirected or wrong-kind target. (A NotFound source is tolerated and hashed as \"absent\"; other IO errors propagate as-is.)","triggerScenarios":"Called from LayoutRetirementV1, retire_verified_legacy_source, or LayoutMigrationMaterialV1 when the source path is a symlink to a directory, a regular file, or another non-directory entry.","commonSituations":"Legacy source directory replaced by a symlink to consolidated storage; migration re-run after the source directory was deleted and a file created in its place; users pointing migration at a file instead of the directory.","solutions":["Replace the symlink at the source path with the real directory (or repoint the migration at the real directory).","Verify the source path with `ls -la` and ensure it is an actual directory, not a file.","If the legacy source is legitimately gone, ensure it is absent (NotFound is accepted) rather than replaced by a wrong-kind entry."],"exampleFix":"// before: source replaced by symlink\nln -s /data/new-layout /data/legacy-layout\n// after: real directory\nrm /data/legacy-layout\nmkdir /data/legacy-layout  # or move the real directory back","handlingStrategy":"validation","validationCode":"let md = std::fs::symlink_metadata(&source_dir)?;\nmatch md {\n    md if md.is_dir() => {},                       // ok\n    _ if md.file_type().is_symlink() => return Err(\"legacy source is a symlink\"),\n    _ => return Err(\"legacy source is not a directory\"),\n}","typeGuard":null,"tryCatchPattern":"match retire_verified_legacy_source(...) {\n    Err(e) if e.to_string().contains(\"source is redirected or not a directory\") => restore_source_directory(),\n    other => other,\n}","preventionTips":["Keep legacy source directories as real directories; never symlink them.","Audit for symlinks with `find <root> -type l` before migrating.","If a legacy source is gone, delete it entirely (absent is accepted) rather than substituting a file."],"tags":["filesystem","symlink","migration","io"],"backgroundTag":"path-is-not-a-directory","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"}