{"record":{"id":"5398fdd1d68de713","repo":"astrid-runtime/astrid","slug":"legacy-env-secret-path-is-not-a-regular-directory","errorCode":null,"errorMessage":"legacy env/secret path is not a regular directory: {}","messagePattern":"legacy env/secret path is not a regular directory: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/storage/migration.rs","lineNumber":347,"sourceCode":"        statuses.push(LegacyEnvSecretImportStatus {\n            uid,\n            alias,\n            native_env_present,\n            native_secret_present,\n            unreceipted_capsules,\n        });\n    }\n    Ok(statuses)\n}\n\nfn legacy_entries_present(path: &Path) -> anyhow::Result<bool> {\n    let metadata = match fs::symlink_metadata(path) {\n        Ok(metadata) => metadata,\n        Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(false),\n        Err(error) => return Err(error).with_context(|| format!(\"inspect {}\", path.display())),\n    };\n    if metadata.file_type().is_symlink() || !metadata.is_dir() {\n        bail!(\n            \"legacy env/secret path is not a regular directory: {}\",\n            path.display()\n        );\n    }\n    let mut entries = fs::read_dir(path).with_context(|| format!(\"read {}\", path.display()))?;\n    Ok(entries.next().transpose()?.is_some())\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::Arc;\n\n    use astrid_core::identity::PrincipalUid;\n    use astrid_storage::{\n        KvQuotaResolver, PrincipalDirectory, StateOwner,\n        open_runtime_principal_store_with_directory,\n    };\n","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage/migration.rs#L329-L365","documentation":"While checking whether legacy env/secret entries exist, the code stats the path with symlink_metadata and requires it to be a real (non-symlink) directory. If the path is a symlink or any non-directory file type, it bails with this error rather than silently treating it as present or absent. This protects against hostile or accidental symlinks pointing the legacy importer at unexpected locations.","triggerScenarios":"legacy_env_secret_import_status → legacy_entries_present is called and the legacy env/secret path is a symlink, a regular file, a FIFO, etc., instead of an actual directory.","commonSituations":"Users replaced the legacy directory with a symlink into a synced/Dotfiles-managed location (e.g. ~/dotfiles/env → ~/.astrid/legacy/env); packaging left a regular file where the directory should be; restore tools replaced directories with symlinks.","solutions":["Replace the symlink with a real directory (move the symlink's target contents into place, or remove the symlink and re-create the directory).","If it is a regular file, remove or rename it and recreate the expected directory.","Re-run the import after the path is a plain directory."],"exampleFix":"// before: legacy path is a symlink → bail\n~/.astrid/legacy/env -> /mnt/dotfiles/env\n\n// after: make it a real directory\nrm ~/.astrid/legacy/env\nmkdir -p ~/.astrid/legacy/env\ncp -a /mnt/dotfiles/env/. ~/.astrid/legacy/env/","handlingStrategy":"validation","validationCode":"let meta = std::fs::symlink_metadata(path)?;\nif meta.file_type().is_symlink() || !meta.is_dir() {\n    eprintln!(\"{} must be a real directory; fix before import\", path.display());\n}","typeGuard":"fn is_regular_dir(path: &std::path::Path) -> bool {\n    std::fs::symlink_metadata(path)\n        .map(|m| m.is_dir() && !m.file_type().is_symlink())\n        .unwrap_or(false)\n}","tryCatchPattern":"match import_env_and_secrets(&home, &directory) {\n    Ok(s) => { /* ... */ }\n    Err(e) if e.to_string().contains(\"not a regular directory\") => {\n        eprintln!(\"replace the symlink/file at the legacy path with a real directory\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Do not symlink legacy env/secret directories into dotfile/sync stores.","Restore from backups as real directories, not symlinks.","Audit legacy paths with `find -type l` before migration.","Document that the importer requires plain directories."],"tags":["symlink","filesystem","legacy-import","validation"],"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"}