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

  1. Read the {detail} suffix in the error; it names the exact violated precondition (non-canonical, not UTF-8, too long, receipt UID not canonical).
  2. Fix the offending legacy source at the printed path: rename to a canonical UTF-8 relative path or repair/remove the malformed receipt file.
  3. Remove or restructure entries containing '..', absolute forms, or non-UTF-8 names under the legacy home, then re-run migration.
  4. 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

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


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