{"record":{"id":"b3915d5d0caa55a0","repo":"astrid-runtime/astrid","slug":"legacy-principal-profile-is-not-a-regular-file","errorCode":null,"errorMessage":"legacy principal profile is not a regular file: {}","messagePattern":"legacy principal profile is not a regular file: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/principal_home_migration/unbound.rs","lineNumber":157,"sourceCode":"        {\n            identity\n                .bind_principal_identity(user.id, alias.clone(), public_key)\n                .await\n                .map_err(|error| identity_io(&error))?;\n        }\n        return Ok(user);\n    }\n    identity\n        .create_principal(alias.clone(), public_key)\n        .await\n        .map_err(|error| identity_io(&error))\n}\n\nfn ensure_profile_with_genesis_key(home: &AstridHome, alias: &PrincipalId) -> io::Result<()> {\n    let path = PrincipalProfile::path_for(home, alias);\n    let mut profile = match fs::symlink_metadata(&path) {\n        Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_file() => {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                format!(\n                    \"legacy principal profile is not a regular file: {}\",\n                    path.display()\n                ),\n            ));\n        },\n        Ok(_) => {\n            PrincipalProfile::load_required(home, alias).map_err(|error| profile_io(&error))?\n        },\n        Err(error) if error.kind() == io::ErrorKind::NotFound => PrincipalProfile::default(),\n        Err(error) => return Err(error),\n    };\n    let minted = mint_bootstrap_keypair(home, alias, &mut profile)?;\n    if minted || !path.is_file() {\n        profile\n            .save(home, alias)\n            .map_err(|error| profile_io(&error))?;","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/principal_home_migration/unbound.rs#L139-L175","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Replace the symlink/special file with a real regular profile file at the expected path.","Remove the offending leftover entry so it is quarantined instead of minted, then re-run migration.","Audit the legacy home for symlinks (find -type l) and flatten them before migrating.","Restore the profile file from backup as a regular file, not a link."],"exampleFix":"// before\nln -s /shared/profiles/ali.json ~/.legacy/profiles/ali.json\n// after\ncp /shared/profiles/ali.json ~/.legacy/profiles/ali.json  # regular file","handlingStrategy":"validation","validationCode":"fn profile_is_regular_file(path: &Path) -> bool {\n    match fs::symlink_metadata(path) {\n        Ok(m) => !m.file_type().is_symlink() && m.is_file(),\n        Err(_) => false,\n    }\n}","typeGuard":null,"tryCatchPattern":"match mint_valid_leftover(entry) {\n    Err(e) if e.to_string().contains(\"not a regular file\") => {\n        // quarantine this leftover instead of minting\n        quarantine(entry)?;\n    }\n    other => other?,\n}","preventionTips":["Audit legacy homes for symlinks (find -type l) and flatten before migration.","Restore backups as regular files, never links.","Do not point profile paths at directories, sockets, or FIFOs.","Let the migration quarantine non-regular leftovers rather than force-minting them."],"tags":["io","filesystem","symlink","security","migration"],"backgroundTag":"incompatible-source-type","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"}