astrid-runtime/astrid · error

legacy env/secret source changed before import

Error message

legacy env/secret source changed before import: {name}

What it means

Before importing, each legacy env/secret source is snapshotted (preflight). At import time the file is re-snapshotted and compared; if it differs from the preflight identity, the library aborts with `InvalidData` to prevent importing a file that changed mid-migration.

Solutions

  1. Stop processes that touch the legacy env files, then re-run the migration.
  2. Quiesce file-sync clients or pause them during migration.
  3. Restore the file to its pre-migration content or redo preflight so snapshots match.

Example fix

# before
astrid migrate &  # while syncd rewrites .env
# after
pause Dropbox; astrid migrate; resume Dropbox
Defensive patterns

Strategy: retry

Validate before calling

let before = snapshot_path(env_file)?;
std::thread::sleep(std::time::Duration::from_millis(200));
let after = snapshot_path(env_file)?;
if before != after { eprintln!("{env_file:?} is being modified; quiesce writers first"); }

Try / catch

loop {
    match migrate_legacy_layout(...) {
        Err(e) if e.to_string().contains("changed before import") => {
            attempts += 1;
            if attempts > 3 { return Err(e.into()); }
            std::thread::sleep(std::time::Duration::from_secs(1));
        }
        other => { other?; break; }
    }
}

Prevention

When it happens

Trigger: Calling `import_env_and_secrets` (via `require_scope_matches_ledger`) when the file's content/metadata changed between preflight snapshot and import; also exercised directly by tests `capsule_scope_import_rejects_source_that_changed_after_preflight` / `..._accepts_identical_...`.

Common situations: A running process (dotenv loader, editor autosave, sync client like Dropbox) rewrote the secret file during migration; user edited `.env` while migration ran; TOCTOU race on network mounts.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

        return Err(io::Error::other(format!(
            "legacy env/secret sources remain for {} (uid {}); migration API did not retire every scope",
            status.alias, status.uid
        )));
    }
    Ok(())
}

fn require_scope_matches_ledger(
    snapshots: &BTreeMap<String, SourceIdentity>,
    name: &str,
    path: &Path,
) -> io::Result<()> {
    let expected = snapshots
        .get(name)
        .ok_or_else(|| io::Error::other(format!("migration source inventory is missing {name}")))?;
    let actual = snapshot_path(path)?;
    if actual != *expected {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("legacy env/secret source changed before import: {name}"),
        ));
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::require_scope_matches_ledger;
    use crate::legacy_migration_barrier::host_fs::snapshot_path;
    use std::collections::BTreeMap;
    use std::fs;

    fn make_private_file(path: &std::path::Path) {
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt as _;

View on GitHub (pinned to affd8760f4)