astrid-runtime/astrid · error
legacy principal-home source {}: {detail}
Error message
legacy principal-home source {}: {detail} What it means
invalid_source builds the canonical 'legacy principal-home source {path}: {detail}' InvalidData error used throughout the principal-home migration. It is the generic rejection of a legacy source path that violates a migration precondition: non-canonical relative paths, non-UTF-8 names, over-long paths, non-canonical receipt UIDs, or non-UTF-8 entry names. The library throws it to fail closed before reading, snapshotting, retiring, or verifying legacy sources, since migrating a malformed source could corrupt the new home layout or receipts.
Source
Thrown at crates/astrid-kernel/src/principal_home_migration/paths.rs:147
[".config", name] => matches!(*name, "profile.toml" | "distro.lock" | "distro.init.lock"),
[".local", name, ..] => {
matches!(
*name,
"capsules" | "audit" | "tmp" | "kv" | "tokens" | "log"
)
},
_ => false,
}
}
pub(super) fn storage_error(error: &FilesystemError) -> io::Error {
io::Error::other(format!(
"authoritative home migration storage error: {error}"
))
}
pub(super) fn invalid_source(path: &Path, detail: &str) -> io::Error {
io::Error::new(
io::ErrorKind::InvalidData,
format!("legacy principal-home source {}: {detail}", path.display()),
)
}
pub(super) fn conflict_path(path: &Path, detail: &str) -> io::Error {
io::Error::new(
io::ErrorKind::AlreadyExists,
format!(
"principal-home migration conflict at {}: {detail}",
path.display()
),
)
}
pub(super) fn conflict_fs(path: &FilesystemPath, detail: &str) -> io::Error {
io::Error::new(
io::ErrorKind::AlreadyExists,View on GitHub (pinned to affd8760f4)
Solutions
- Read the {detail} suffix in the error; it names the exact violated precondition (non-canonical, not UTF-8, too long, receipt UID not canonical).
- Fix the offending legacy source at the printed path: rename to a canonical UTF-8 relative path or repair/remove the malformed receipt file.
- Remove or restructure entries containing '..', absolute forms, or non-UTF-8 names under the legacy home, then re-run migration.
- If a receipt is corrupt and no migration is in flight, delete the stale receipt so receipt_uid_for_alias no longer rejects it.
Example fix
// before: non-UTF-8 name in legacy home $ rm './legacy/feff.txt' # filename with invalid UTF-8 bytes // after: rename to a valid UTF-8 canonical name $ mv './legacy/feff.txt' './legacy/legacy-note.txt' $ # re-run the principal-home migration
Defensive patterns
Strategy: validation
Validate before calling
fn validate_legacy_source(path: &std::path::Path) -> Result<(), String> {
if path.is_absolute() { return Err("absolute".into()); }
if !path.components().all(|c| matches!(c, std::path::Component::Normal(_))) { return Err("non-canonical".into()); }
let s = path.to_str().ok_or("not UTF-8")?;
if s.replace('\\', "/").len() > MAX_RELATIVE_PATH_BYTES { return Err("too long".into()); }
Ok(())
} Type guard
fn valid_receipt_name(name: &str) -> Option<(astrid_core::PrincipalUid, &str)> {
let uid_text = name.strip_prefix(RECEIPT_PREFIX)?.strip_suffix(RECEIPT_SUFFIX)?;
if uid_text.contains(RECEIPT_PAGE_MARKER) { return None; }
Some((uid_text.parse().ok()?, uid_text))
} Try / catch
match legacy_ordinary_source_snapshot(&source) {
Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
let detail = e.to_string();
eprintln!("fix legacy source before migrating: {detail}");
// surface detail (non-UTF-8 / too long / non-canonical) to the operator
}
other => other?,
} Prevention
- Audit legacy trees with a scanner that rejects non-UTF-8 names and '..'/absolute components before migration day.
- Never hand-edit migration receipt files; regenerate them via the library.
- Keep legacy path depth/naming within MAX_RELATIVE_PATH_BYTES.
- Use fs::read_dir result file_name().to_str() checks in your own tooling to catch bad names early.
When it happens
Trigger: Any of migrate_legacy_principal_homes, legacy_ordinary_source_snapshot, verify_migrated_legacy_principal_sources_retired, retire_one_receipted_source, retire_empty_tree, or walk_inventory encountering a legacy source path/receipt whose detail check fails — e.g. absolute or '..'-containing legacy relative paths (logical_relative), non-UTF-8 file names (append_relative), receipt file names whose UID portion does not parse canonically (receipt_uid_for_alias), or paths exceeding MAX_RELATIVE_PATH_BYTES.
Common situations: Legacy home trees produced by older versions with Windows-style or non-normalized paths; files created with non-UTF-8 byte names on permissive filesystems; hand-edited or copied migration receipt files in the migrations directory; extremely deep legacy directory trees exceeding the byte limit.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- legacy path {text:?} is not a canonical filesystem path: {er
- invalid logical destination {}: {error}
- legacy audit source has no parent
- WouldBlock
- legacy audit retirement source is outside the default princi
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/805aa62d905f0711.
Report an issue: GitHub.