{"record":{"id":"e57745f3ed9ab087","repo":"astrid-runtime/astrid","slug":"decode-legacy-log-receipt-error","errorCode":null,"errorMessage":"decode legacy log receipt {}: {error}","messagePattern":"decode legacy log receipt (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/principal_log_migration.rs","lineNumber":443,"sourceCode":"        if metadata.file_type().is_symlink() || !metadata.is_file() {\n            return Err(invalid(path, \"legacy log receipt is not a regular file\"));\n        }\n        astrid_core::platform_fs::verify_no_redirects(path)?;\n        astrid_core::platform_fs::validate_private_file(path)?;\n        if metadata.len() > MAX_RECEIPT_BYTES {\n            return Err(invalid(path, \"legacy log receipt exceeds size limit\"));\n        }\n    }\n    match fs::read(path) {\n        Ok(bytes) => Ok(Some(bytes)),\n        Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(None),\n        Err(error) => Err(error),\n    }\n}\n\nfn decode_receipt(bytes: &[u8], path: &Path) -> io::Result<LogReceipt> {\n    let receipt: LogReceipt = serde_json::from_slice(bytes).map_err(|error| {\n        io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"decode legacy log receipt {}: {error}\", path.display()),\n        )\n    })?;\n    if canonical_json(&receipt)? != bytes {\n        return Err(invalid(path, \"legacy log receipt is not canonical JSON\"));\n    }\n    Ok(receipt)\n}\n\nfn canonical_json<T: Serialize>(value: &T) -> io::Result<Vec<u8>> {\n    serde_json::to_vec(value).map_err(io::Error::other)\n}\n\nfn digest_file(path: &Path, expected_device: u64) -> io::Result<(u64, String)> {\n    let metadata = fs::symlink_metadata(path)?;\n    if metadata.file_type().is_symlink()\n        || !metadata.is_file()","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/principal_log_migration.rs#L425-L461","documentation":"The legacy log receipt file could not be parsed as JSON into a `LogReceipt`, or (subsequently) its canonical JSON re-encoding does not byte-match the stored bytes. The kernel treats receipts as strictly canonical JSON, so any whitespace/ordering difference or corrupt content is rejected with InvalidData and the path plus serde error in the message.","triggerScenarios":"`decode_receipt` is called by `legacy_log_destination_proof` and `migrate_one` on bytes read from the receipt file; serde_json::from_slice fails (truncated file, binary garbage, partial write) or `canonical_json(&receipt) != bytes`.","commonSituations":"Crash or power loss mid-write leaving a truncated receipt; the file was edited with an editor that reformatted JSON; disk corruption; an incompatible receipt format from a very old version.","solutions":["Delete the corrupt receipt file and re-run the migration so it is rewritten","Check the serde error in the message to see whether it is truncation vs. wrong structure","Restore the receipt from a known-good backup","Never reformat receipt JSON by hand — the kernel requires byte-exact canonical form"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match decode_receipt(&bytes, &path) {\n    Ok(r) => use_receipt(r),\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => {\n        // corrupt/non-canonical receipt: regenerate\n        std::fs::remove_file(&path)?;\n        re_run_migration()?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Don't open receipts in editors that reformat JSON — canonical bytes are required","Shut down cleanly; crashes mid-write can truncate receipts","Keep backups of the home before migrating"],"tags":["json","parsing","receipt","corruption"],"backgroundTag":"json-decode-failed","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"}