{"record":{"id":"2fab857efdd87b59","repo":"astrid-runtime/astrid","slug":"parse-runtime-tree-receipt-error","errorCode":null,"errorMessage":"parse runtime tree receipt: {error}","messagePattern":"parse runtime tree receipt: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-kernel/src/runtime_tree_admit.rs","lineNumber":198,"sourceCode":"    if !metadata.is_file() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\n                \"runtime tree receipt is not a regular file: {}\",\n                path.display()\n            ),\n        ));\n    }\n    if metadata.len() > MAX_RECEIPT_BYTES {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"runtime tree receipt exceeds {MAX_RECEIPT_BYTES} bytes\"),\n        ));\n    }\n    astrid_core::platform_fs::validate_private_file(path)?;\n    let bytes = fs::read(path)?;\n    serde_json::from_slice(&bytes).map(Some).map_err(|error| {\n        io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"parse runtime tree receipt: {error}\"),\n        )\n    })\n}\n\nfn storage_error(error: impl std::error::Error + Send + Sync + 'static) -> io::Error {\n    io::Error::other(error)\n}\n\n#[cfg(test)]\ntype SourceMutationHook = Box<dyn FnOnce(&Path) -> io::Result<()> + Send + 'static>;\n\n#[cfg(test)]\nstatic SOURCE_MUTATION_HOOK: std::sync::OnceLock<\n    std::sync::Mutex<Option<(std::path::PathBuf, SourceMutationHook)>>,\n> = std::sync::OnceLock::new();\n","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-kernel/src/runtime_tree_admit.rs#L180-L216","documentation":"The runtime tree receipt file passed the size and type checks but its bytes are not valid JSON matching the expected receipt structure. The serde error is embedded in the message. This guards against corrupted or foreign files at the receipt path being interpreted as a valid receipt.","triggerScenarios":"`read_receipt` runs `serde_json::from_slice(&bytes)` on the file contents after `fs::read`; parsing fails due to truncation, non-JSON content, or a receipt shape that doesn't deserialize into the expected type.","commonSituations":"Partial write from a crash; someone replaced the receipt with a different JSON file; schema drift between app versions where fields changed shape; corruption on disk.","solutions":["Delete the unreadable receipt and re-run admission so a fresh one is written (a missing receipt is treated as Ok(None))","Read the embedded serde message to see the exact JSON path/type that failed","Restore the receipt from backup if it carries state you need","Ensure the app version reading the receipt matches the version that wrote it"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// cheap sanity check before handing the file to the kernel\nif std::fs::read(&receipt_path)\n    .map(|b| serde_json::from_slice::<serde_json::Value>(&b).is_ok())\n    .unwrap_or(false) { /* proceed */ } else { std::fs::remove_file(&receipt_path)?; }","typeGuard":null,"tryCatchPattern":"match err.downcast_ref::<io::Error>() {\n    Some(e) if e.kind() == io::ErrorKind::InvalidData && msg.contains(\"parse runtime tree receipt\") => {\n        std::fs::remove_file(&path)?; // regenerate\n        retry_admit()?;\n    }\n    _ => return Err(err),\n}","preventionTips":["Never hand-edit receipt JSON","Shut down cleanly to avoid truncated writes","Restore receipts only from matching-version backups"],"tags":["json","parsing","receipt"],"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"}