{"record":{"id":"8270357aa4c4ec80","repo":"astrid-runtime/astrid","slug":"cannot-migrate-an-astrid-home-without-a-layout-ver","errorCode":null,"errorMessage":"cannot migrate an Astrid home without a layout-version sentinel","messagePattern":"cannot migrate an Astrid home without a layout-version sentinel","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/dirs_layout.rs","lineNumber":166,"sourceCode":"        self.var_dir().join(\"migrations\")\n    }\n\n    /// Persist the content-bound layout migration intent before opening stores.\n    ///\n    /// The caller must hold the daemon singleton lock. Re-entry accepts only\n    /// the exact same source inventory, destination format, physical roots, and\n    /// executable identity.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error for unsupported layouts, redirected paths, invalid\n    /// source content, or a prior intent for a different transaction.\n    pub fn begin_layout_v2_migration(&self, target: &LayoutMigrationTarget) -> io::Result<()> {\n        match self.layout_version()?.as_deref() {\n            Some(LAYOUT_VERSION) => return Ok(()),\n            Some(LEGACY_LAYOUT_VERSION) => {},\n            None => {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    \"cannot migrate an Astrid home without a layout-version sentinel\",\n                ));\n            },\n            Some(other) => {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\"unsupported Astrid home layout version {other:?}\"),\n                ));\n            },\n        }\n        reject_automatic_windows_layout_one()?;\n        self.preflight_layout_v2_paths()?;\n        for path in [\n            self.storage_volume_path(),\n            self.legacy_storage_volume_path(),\n            self.retired_root_storage_volume_path(),\n        ] {","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/dirs_layout.rs#L148-L184","documentation":"begin_layout_v2_migration in crates/astrid-core/src/dirs_layout.rs:166 refuses to start a layout-v2 migration when the Astrid home has no layout-version sentinel file at all (layout_version() returns None). Migration only runs from a known legacy version (LEGACY_LAYOUT_VERSION); a home with no sentinel is unrecognized state, so the operation fails with InvalidData to avoid guessing the layout.","triggerScenarios":"Calling begin_layout_v2_migration on an Astrid home directory that was never initialized by any version (sentinel never written), or where the sentinel file was deleted/corrupted so it cannot be read.","commonSituations":"Running migration against a blank/fresh home directory by mistake; manual cleanup or disk-cleanup tools deleting the version sentinel; restoring a partial backup that omitted the sentinel file.","solutions":["Initialize the Astrid home first (run the app/installer's init) so a valid layout-version sentinel is written, then migrate if needed.","Restore the missing layout-version sentinel from backup if the home content is otherwise intact.","Confirm you are pointing at the correct home directory — a fresh/empty home cannot be migrated.","If the home is genuinely uninitialized, no migration is needed; skip begin_layout_v2_migration."],"exampleFix":"// before\nlet dirs = AstridDirs::at(empty_home)?;\ndirs.begin_layout_v2_migration(&target)?; // InvalidData: no sentinel\n\n// after\nif dirs.layout_version()?.is_none() {\n    dirs.initialize_home()?; // writes layout-version sentinel\n}\ndirs.begin_layout_v2_migration(&target)?;","handlingStrategy":"validation","validationCode":"// Rust\nfn migration_possible(dirs: &AstridDirs) -> io::Result<bool> {\n    Ok(dirs.layout_version()?.is_some()) // None means begin_layout_v2_migration will fail\n}","typeGuard":null,"tryCatchPattern":"match home.layout_version() {\n    Ok(None) => {\n        // no sentinel: initialize the home first or skip migration entirely\n    }\n    Ok(Some(v)) if v == LAYOUT_VERSION => { /* already migrated */ }\n    Ok(Some(_)) => home.begin_layout_v2_migration(&target)?,\n    Err(e) => return Err(e),\n}","preventionTips":["Check layout_version() is Some before attempting migration","Never delete the layout-version sentinel during cleanup","Restore the sentinel from backup if a partial restore omitted it","Initialize fresh homes through the normal init flow instead of migrating them"],"tags":["migration","layout","state","validation"],"backgroundTag":"invalid-state-transition","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}