astrid-runtime/astrid · error

legacy env source is not a regular file

Error message

legacy env source is not a regular file: {}

What it means

During legacy layout migration, each entry under the legacy env root must be a regular file (env or secret payload). Symlinks and directories are rejected with `InvalidData` because the migration deliberately hard-depends on real files rather than guessing through aliases.

Solutions

  1. Replace the symlinked/directory entry with a real regular file copy.
  2. Move unrelated directories out of the legacy env root.
  3. Re-run the migration after the env root contains only regular files.

Example fix

# before
.env -> ~/dotfiles/.env  (symlink)
# after
rm .env && cp ~/dotfiles/.env .env
Defensive patterns

Strategy: validation

Validate before calling

fn legacy_env_ready(root: &std::path::Path) -> bool {
    std::fs::read_dir(root).map(|rd| rd.filter_map(Result::ok).all(|e| {
        std::fs::symlink_metadata(e.path()).map(|m| m.is_file() && !m.file_type().is_symlink()).unwrap_or(false)
    })).unwrap_or(false)
}

Type guard

fn is_regular_file(p: &std::path::Path) -> bool {
    std::fs::symlink_metadata(p).map(|m| m.is_file() && !m.file_type().is_symlink()).unwrap_or(false)
}

Try / catch

match migrate_legacy_layout(...) {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData
        && e.to_string().contains("not a regular file") => {
        eprintln!("replace symlinks/dirs in legacy env root with real files");
        return Err(e.into());
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling `migrate_legacy_layout` → `import_env_and_secrets` when `read_dir` on the env root yields an entry whose `symlink_metadata` is not a plain file (symlink, directory, fifo, etc.).

Common situations: Users symlinked legacy secret files into a dotfiles manager (e.g. GNU Stow/chezmoi); a directory was created where a secret file should be; leftover temp directories in the legacy env root.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-kernel/src/legacy_migration_barrier/env_import.rs:38

    store: &RuntimePrincipalStore,
    bindings: &[(PrincipalId, PrincipalUid)],
    snapshots: &BTreeMap<String, SourceIdentity>,
    host_secret_source: &SourceIdentity,
) -> io::Result<()> {
    let handle = tokio::runtime::Handle::current();
    for (alias, uid) in bindings {
        let owner = astrid_storage::StateOwner::Principal(*uid);
        let summaries = store.capsules().list(&owner).map_err(storage_io)?;
        let env_root = home.principal_home(alias).env_dir();
        let secret_root = home.secrets_dir().join(alias.as_str());
        // Unknown capsule-specific files cannot be assigned safely.  This is
        // deliberately a hard dependency rather than an alias-based guess.
        if path_exists(&env_root)? {
            let mut entries = fs::read_dir(&env_root).map_err(io::Error::other)?;
            while let Some(entry) = entries.next().transpose().map_err(io::Error::other)? {
                let metadata = fs::symlink_metadata(entry.path()).map_err(io::Error::other)?;
                if !metadata.is_file() {
                    return Err(io::Error::new(
                        io::ErrorKind::InvalidData,
                        format!(
                            "legacy env source is not a regular file: {}",
                            entry.path().display()
                        ),
                    ));
                }
            }
        }
        for summary in summaries {
            let capsule = summary.id();
            let env = env_root.join(format!("{capsule}.env.json"));
            let secret = secret_root.join(capsule);
            require_scope_matches_ledger(
                snapshots,
                &format!("principal:{uid}:env:{capsule}"),
                &env,
            )?;

View on GitHub (pinned to affd8760f4)