astrid-runtime/astrid · error

legacy principal profile is not a regular file: {}

Error message

legacy principal profile is not a regular file: {}

What it means

Thrown by ensure_profile_with_genesis_key when the legacy principal profile file is a symlink or not a regular file. During minting of a valid leftover into a new identity, the profile is read to extract the genesis key, and links or special files are rejected to prevent redirect-based attacks. Full symlink metadata is inspected rather than following the link.

Source

Thrown at crates/astrid-kernel/src/principal_home_migration/unbound.rs:157

        {
            identity
                .bind_principal_identity(user.id, alias.clone(), public_key)
                .await
                .map_err(|error| identity_io(&error))?;
        }
        return Ok(user);
    }
    identity
        .create_principal(alias.clone(), public_key)
        .await
        .map_err(|error| identity_io(&error))
}

fn ensure_profile_with_genesis_key(home: &AstridHome, alias: &PrincipalId) -> io::Result<()> {
    let path = PrincipalProfile::path_for(home, alias);
    let mut profile = match fs::symlink_metadata(&path) {
        Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_file() => {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "legacy principal profile is not a regular file: {}",
                    path.display()
                ),
            ));
        },
        Ok(_) => {
            PrincipalProfile::load_required(home, alias).map_err(|error| profile_io(&error))?
        },
        Err(error) if error.kind() == io::ErrorKind::NotFound => PrincipalProfile::default(),
        Err(error) => return Err(error),
    };
    let minted = mint_bootstrap_keypair(home, alias, &mut profile)?;
    if minted || !path.is_file() {
        profile
            .save(home, alias)
            .map_err(|error| profile_io(&error))?;

View on GitHub (pinned to affd8760f4)

Solutions

  1. Replace the symlink/special file with a real regular profile file at the expected path.
  2. Remove the offending leftover entry so it is quarantined instead of minted, then re-run migration.
  3. Audit the legacy home for symlinks (find -type l) and flatten them before migrating.
  4. Restore the profile file from backup as a regular file, not a link.

Example fix

// before
ln -s /shared/profiles/ali.json ~/.legacy/profiles/ali.json
// after
cp /shared/profiles/ali.json ~/.legacy/profiles/ali.json  # regular file
Defensive patterns

Strategy: validation

Validate before calling

fn profile_is_regular_file(path: &Path) -> bool {
    match fs::symlink_metadata(path) {
        Ok(m) => !m.file_type().is_symlink() && m.is_file(),
        Err(_) => false,
    }
}

Try / catch

match mint_valid_leftover(entry) {
    Err(e) if e.to_string().contains("not a regular file") => {
        // quarantine this leftover instead of minting
        quarantine(entry)?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: ensure_profile_with_genesis_key (called from mint_valid_leftover) calls fs::symlink_metadata on PrincipalProfile::path_for(home, alias) and finds the path is a symlink or not a regular file (directory, socket, FIFO, device).

Common situations: A leftover entry in the legacy home is actually a directory (e.g. a nested home) rather than a profile file; symlinks were used to organize legacy homes; a file was replaced by a socket/FIFO by another tool; partially restored backups left links behind.

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/b3915d5d0caa55a0. Report an issue: GitHub.