{"record":{"id":"03bcdf4f98351513","repo":"astrid-runtime/astrid","slug":"invalid-receipt-page-number-error","errorCode":null,"errorMessage":"invalid receipt page number: {error}","messagePattern":"invalid receipt page number: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/principal_home_migration/receipts.rs","lineNumber":262,"sourceCode":"            Ok(page)\n        },\n    }\n}\n\nfn page_number(path: &Path) -> io::Result<u64> {\n    let name = path\n        .file_name()\n        .and_then(|name| name.to_str())\n        .ok_or_else(|| invalid_source(path, \"receipt page name is not UTF-8\"))?;\n    let uid_and_page = name\n        .strip_prefix(RECEIPT_PREFIX)\n        .and_then(|name| name.strip_suffix(RECEIPT_SUFFIX))\n        .ok_or_else(|| invalid_source(path, \"receipt page name is not canonical\"))?;\n    let (_, number) = uid_and_page\n        .rsplit_once(RECEIPT_PAGE_MARKER)\n        .ok_or_else(|| invalid_source(path, \"receipt page name is not canonical\"))?;\n    number.parse().map_err(|error| {\n        io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"invalid receipt page number: {error}\"),\n        )\n    })\n}\n\npub(super) fn remove_stale_pages(home: &AstridHome, uid: PrincipalUid) -> io::Result<()> {\n    let directory = home.migrations_dir();\n    let prefix = format!(\"{RECEIPT_PREFIX}{uid}{RECEIPT_PAGE_MARKER}\");\n    for entry in fs::read_dir(directory)? {\n        let entry = entry?;\n        let name = entry.file_name();\n        let Some(name) = name.to_str() else {\n            continue;\n        };\n        if !name.starts_with(&prefix) || !name.ends_with(RECEIPT_SUFFIX) {\n            continue;\n        }","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/principal_home_migration/receipts.rs#L244-L280","documentation":"Thrown by page_number when the numeric portion of a receipt page filename fails to parse. The function strips the receipt suffix, splits on the page marker to isolate the number, then parses it; anything non-numeric is rejected. This guards the receipts directory against non-canonical or garbage filenames.","triggerScenarios":"read_page resolves a filename where the text after the last RECEIPT_PAGE_MARKER (before RECEIPT_SUFFIX) is not a valid integer — letters, empty segment, or a value overflowing the target integer type.","commonSituations":"A user manually created or renamed page files; editors or sync tools left temp files like page-0001.json.crash partially matching; a filename from a different naming scheme/version is present.","solutions":["Rename or remove the offending file so the directory contains only canonical page names (uid + marker + numeric page + suffix).","List the directory and confirm every file's trailing segment is a small non-negative integer; fix the one failing to parse.","Do not manually manage receipt pages; let migration tooling create them.","Exclude the receipts directory from editors/sync tools that create temp files."],"exampleFix":"// before\nreceipt-<uid>-page-abc.json   // parse fails\n// after\nreceipt-<uid>-page-0001.json","handlingStrategy":"validation","validationCode":"fn page_name_is_canonical(name: &str, uid: &str) -> bool {\n    name.strip_suffix(RECEIPT_SUFFIX)\n        .and_then(|s| s.rsplit_once(RECEIPT_PAGE_MARKER))\n        .map(|(u, n)| u == uid && n.parse::<u32>().is_ok())\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match page_number(path) {\n    Ok(n) => use(n),\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => {\n        // non-canonical filename: quarantine/remove the file, then rescan\n        let _ = fs::remove_file(path);\n        rescan_receipts()?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Never manually create or rename receipt page files.","Exclude the receipts directory from editors/sync tools that leave temp files.","Validate directory listings for canonical names before running verification.","Let migration tooling own the entire receipts directory."],"tags":["io","filename","migration","rust"],"backgroundTag":"invalid-identifier-format","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"}