{"record":{"id":"e5c40333cc2405b1","repo":"astrid-runtime/astrid","slug":"legacy-env-secret-source-changed-before-import-n","errorCode":null,"errorMessage":"legacy env/secret source changed before import: {name}","messagePattern":"legacy env/secret source changed before import: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/legacy_migration_barrier/env_import.rs","lineNumber":104,"sourceCode":"        return Err(io::Error::other(format!(\n            \"legacy env/secret sources remain for {} (uid {}); migration API did not retire every scope\",\n            status.alias, status.uid\n        )));\n    }\n    Ok(())\n}\n\nfn require_scope_matches_ledger(\n    snapshots: &BTreeMap<String, SourceIdentity>,\n    name: &str,\n    path: &Path,\n) -> io::Result<()> {\n    let expected = snapshots\n        .get(name)\n        .ok_or_else(|| io::Error::other(format!(\"migration source inventory is missing {name}\")))?;\n    let actual = snapshot_path(path)?;\n    if actual != *expected {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"legacy env/secret source changed before import: {name}\"),\n        ));\n    }\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::require_scope_matches_ledger;\n    use crate::legacy_migration_barrier::host_fs::snapshot_path;\n    use std::collections::BTreeMap;\n    use std::fs;\n\n    fn make_private_file(path: &std::path::Path) {\n        #[cfg(unix)]\n        {\n            use std::os::unix::fs::PermissionsExt as _;","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/legacy_migration_barrier/env_import.rs#L86-L122","documentation":"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.","triggerScenarios":"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_...`.","commonSituations":"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.","solutions":["Stop processes that touch the legacy env files, then re-run the migration.","Quiesce file-sync clients or pause them during migration.","Restore the file to its pre-migration content or redo preflight so snapshots match."],"exampleFix":"# before\nastrid migrate &  # while syncd rewrites .env\n# after\npause Dropbox; astrid migrate; resume Dropbox","handlingStrategy":"retry","validationCode":"let before = snapshot_path(env_file)?;\nstd::thread::sleep(std::time::Duration::from_millis(200));\nlet after = snapshot_path(env_file)?;\nif before != after { eprintln!(\"{env_file:?} is being modified; quiesce writers first\"); }","typeGuard":null,"tryCatchPattern":"loop {\n    match migrate_legacy_layout(...) {\n        Err(e) if e.to_string().contains(\"changed before import\") => {\n            attempts += 1;\n            if attempts > 3 { return Err(e.into()); }\n            std::thread::sleep(std::time::Duration::from_secs(1));\n        }\n        other => { other?; break; }\n    }\n}","preventionTips":["Pause file-sync clients and editors during migration.","Stop apps that rewrite .env files (dotenv watchers, secret rotators) before migrating.","Run migration on local filesystems, not network mounts prone to metadata churn."],"tags":["migration","consistency","race-condition"],"backgroundTag":"internal-invariant-violation","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}