{"record":{"id":"ad6a751697852baf","repo":"astrid-runtime/astrid","slug":"invalid-principal-home-migration-page-error","errorCode":null,"errorMessage":"invalid principal-home migration page: {error}","messagePattern":"invalid principal-home migration page: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/principal_home_migration/receipts.rs","lineNumber":223,"sourceCode":"    page: u64,\n) -> io::Result<MigrationPage> {\n    let path = page_path(home, uid, page);\n    match fs::symlink_metadata(&path) {\n        Err(error) => Err(error),\n        Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_file() => {\n            Err(invalid_source(&path, \"receipt page is not a regular file\"))\n        },\n        Ok(_) => {\n            astrid_core::platform_fs::validate_private_file(&path)?;\n            let bytes = fs::read(&path)?;\n            if bytes.len() > MAX_RECEIPT_PAGE_BYTES {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\"principal-home migration page exceeds {MAX_RECEIPT_PAGE_BYTES} bytes\"),\n                ));\n            }\n            let page: MigrationPage = serde_json::from_slice(&bytes).map_err(|error| {\n                io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\"invalid principal-home migration page: {error}\"),\n                )\n            })?;\n            if page.schema != ReceiptSchema::V2\n                || page.uid != uid\n                || page.page != page_number(&path)?\n            {\n                return Err(invalid_source(\n                    &path,\n                    \"receipt page identity is not canonical\",\n                ));\n            }\n            if page.entries.len() > PAGE_ENTRY_LIMIT {\n                return Err(invalid_source(&path, \"receipt page has too many entries\"));\n            }\n            let canonical = canonical_json(&page)?;\n            if bytes != canonical {","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/principal_home_migration/receipts.rs#L205-L241","documentation":"Thrown by read_page when a receipt page's bytes do not deserialize into a MigrationPage via serde_json. Parsing itself failed; subsequent checks (schema must be ReceiptSchema::V2, uid must match) only run after successful parse. The library refuses to interpret malformed page data as migration state.","triggerScenarios":"read_page (via retire_one_receipted_source, verify_destinations, validate_receipt_pages) parses a page file containing invalid JSON, wrong field types, or fields missing from MigrationPage.","commonSituations":"Truncated page write after a crash or power loss; manual edits to page files; pages written by an incompatible schema version; copying pages between different homes or code versions.","solutions":["Run jq (or similar) against the page file to read the serde error and correct or delete the malformed page.","Delete the invalid page and re-run the migration so a fresh page is written.","Ensure the reading binary and the writing binary agree on the MigrationPage schema.","Only create/modify pages via the library's write paths (canonical JSON, atomic write)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"fn page_is_parseable(bytes: &[u8]) -> bool {\n    serde_json::from_slice::<MigrationPage>(bytes).is_ok()\n}","typeGuard":null,"tryCatchPattern":"match read_page(path, uid) {\n    Ok(p) => use(p),\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => {\n        let _ = fs::remove_file(path); // discard malformed page\n        re_run_migration_for(uid)?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Only write pages via the library's canonical-JSON atomic writer.","Keep writer and reader binaries on the same schema version.","Back up pages before schema upgrades.","Never hand-edit page JSON."],"tags":["io","serialization","migration","rust"],"backgroundTag":"json-unmarshal-failed","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"}