{"record":{"id":"90c2e0d615a07998","repo":"astrid-runtime/astrid","slug":"layout-migration-intent-is-missing","errorCode":null,"errorMessage":"layout migration intent is missing: {}","messagePattern":"layout migration intent is missing: (.+?)","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-core/src/dirs_layout_records.rs","lineNumber":141,"sourceCode":"                        path.display()\n                    ),\n                )\n            })?;\n            if parsed != *expected || actual != expected_bytes {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\n                        \"layout migration record does not match this transaction: {}\",\n                        path.display()\n                    ),\n                ));\n            }\n            Ok(())\n        },\n        Err(error) if error.kind() == io::ErrorKind::NotFound && allow_create => {\n            super::atomic_write(path, &expected_bytes)\n        },\n        Err(error) if error.kind() == io::ErrorKind::NotFound => Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"layout migration intent is missing: {}\", path.display()),\n        )),\n        Err(error) => Err(error),\n    }\n}\n\npub(super) fn read_canonical_record<T>(path: &Path) -> io::Result<T>\nwhere\n    T: DeserializeOwned + PartialEq + Serialize,\n{\n    let actual = std::fs::read(path)?;\n    let parsed: T = serde_json::from_slice(&actual).map_err(|error| {\n        io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\n                \"invalid layout migration record {}: {error}\",\n                path.display()","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-core/src/dirs_layout_records.rs#L123-L159","documentation":"admit_or_write_canonical has two modes: allow_create (write the expected record atomically) and strict mode where the record must already exist. In strict mode, if the record file is not found (io::ErrorKind::NotFound), it raises InvalidData \"layout migration intent is missing: {path}\" — the caller is trying to continue or complete a migration whose begin/intent record was never written or has been deleted.","triggerScenarios":"complete_layout_v2 (or another strict caller) runs while the intent/record file at `path` does not exist, i.e. the NotFound arm with allow_create == false is taken.","commonSituations":"Calling complete_layout_v2 without ever calling begin_layout_v2_migration; the record file was deleted by cleanup scripts or the user; running the completion step on a different data directory than the one the migration began in.","solutions":["Call begin_layout_v2_migration first so the intent record is written, then complete_layout_v2.","Point the completion call at the same directory that holds the record created during begin (check the path in the message).","Restore the missing record from backup if begin already ran and the file was lost.","Do not delete files under the layout directory between begin and complete."],"exampleFix":"// before\ncomplete_layout_v2(&dir)?; // record never written\n// after\nbegin_layout_v2_migration(&dir)?;\ncomplete_layout_v2(&dir)?;","handlingStrategy":"validation","validationCode":"if !record_path.exists() {\n    return Err(format!(\"migration intent record missing at {} — run begin first\", record_path.display()));\n}","typeGuard":null,"tryCatchPattern":"match complete_layout_v2(&dir) {\n    Err(e) if e.kind() == io::ErrorKind::InvalidData && e.to_string().contains(\"intent is missing\") => {\n        begin_layout_v2_migration(&dir)?;\n        complete_layout_v2(&dir)?;\n    },\n    other => other?,\n}","preventionTips":["Always pair begin_layout_v2_migration with complete_layout_v2 on the same directory","Exclude layout record files from cleanup/backup-restore jobs","Verify record_path.exists() before calling completion APIs"],"tags":["migration","file-not-found","state-machine"],"backgroundTag":"file-not-found","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"}