astrid-runtime/astrid · error
parse runtime tree receipt
Error message
parse runtime tree receipt: {error} What it means
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.
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
Defensive patterns
Strategy: try-catch
Validate before calling
// cheap sanity check before handing the file to the kernel
if std::fs::read(&receipt_path)
.map(|b| serde_json::from_slice::<serde_json::Value>(&b).is_ok())
.unwrap_or(false) { /* proceed */ } else { std::fs::remove_file(&receipt_path)?; } Try / catch
match err.downcast_ref::<io::Error>() {
Some(e) if e.kind() == io::ErrorKind::InvalidData && msg.contains("parse runtime tree receipt") => {
std::fs::remove_file(&path)?; // regenerate
retry_admit()?;
}
_ => return Err(err),
} Prevention
- Never hand-edit receipt JSON
- Shut down cleanly to avoid truncated writes
- Restore receipts only from matching-version backups
When it happens
Trigger: `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.
Common situations: 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.
Related errors
- decode legacy log receipt
- byte value must be non-negative and finite
- decode migration ledger
- empty byte specifier
- empty duration
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/2fab857efdd87b59.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-kernel/src/runtime_tree_admit.rs:198
if !metadata.is_file() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"runtime tree receipt is not a regular file: {}",
path.display()
),
));
}
if metadata.len() > MAX_RECEIPT_BYTES {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("runtime tree receipt exceeds {MAX_RECEIPT_BYTES} bytes"),
));
}
astrid_core::platform_fs::validate_private_file(path)?;
let bytes = fs::read(path)?;
serde_json::from_slice(&bytes).map(Some).map_err(|error| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("parse runtime tree receipt: {error}"),
)
})
}
fn storage_error(error: impl std::error::Error + Send + Sync + 'static) -> io::Error {
io::Error::other(error)
}
#[cfg(test)]
type SourceMutationHook = Box<dyn FnOnce(&Path) -> io::Result<()> + Send + 'static>;
#[cfg(test)]
static SOURCE_MUTATION_HOOK: std::sync::OnceLock<
std::sync::Mutex<Option<(std::path::PathBuf, SourceMutationHook)>>,
> = std::sync::OnceLock::new();
View on GitHub (pinned to affd8760f4)