astrid-runtime/astrid · error
destination proof is missing its payload
Error message
destination proof is missing its payload
What it means
A destination proof string in the component migration ledger used a known `verified-*` or `fresh-layout-v1:` prefix, but the payload after the first `:` was empty. `DestinationProof::parse` requires `prefix:payload` form; `from_stored` surfaces this as an InvalidData io::Error. The ledger's proof receipt is structurally malformed and cannot be trusted as evidence that a migration destination was verified.
Solutions
- Restore the full proof string including its payload (e.g. `verified-empty-v1:<digest-or-id>`) in the migration ledger.
- Regenerate the ledger by re-running the migration/verification step that produced the destination proof.
- If the ledger is unrecoverable, remove it and re-run the component import so fresh proofs are written.
- Never construct proof strings by hand; use `DestinationProof::from_hashed_bytes` or the preset constructors.
Example fix
// before let proof = "verified-empty-v1:"; // missing payload // after let proof = DestinationProof::from_hashed_bytes(&bytes).to_string(); // e.g. "blake3:<64 hex chars>"
Defensive patterns
Strategy: validation
Validate before calling
fn proof_looks_well_formed(s: &str) -> bool {
let Some((prefix, rest)) = s.split_once(':') else { return false };
rest.len() > 0 && (prefix == "blake3" || prefix.starts_with("verified-") || prefix == "fresh-layout-v1")
}
assert!(proof_looks_well_formed(&stored_proof), "ledger proof malformed"); Type guard
fn has_proof_payload(p: &str) -> bool {
p.split_once(':').map(|(_, rest)| !rest.is_empty()).unwrap_or(false)
} Try / catch
match DestinationProof::from_stored(value) {
Ok(proof) => /* ... */,
Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
eprintln!("corrupt ledger proof ({e}); regenerate the migration ledger");
}
Err(e) => return Err(e),
} Prevention
- Never hand-edit migration ledger JSON; use the provided constructors.
- Write the ledger atomically so partial proofs never persist.
- Validate ledger contents after any external tooling touches it.
When it happens
Trigger: Calling `DestinationProof::from_stored` (directly or via ledger decode) on a stored proof value such as `"verified-empty-v1:"` with nothing after the colon. Caused by hand-editing the ledger JSON, truncated writes, or tooling that wrote the prefix without computing the proof payload.
Common situations: Manual ledger edits during debugging; a migration tool that crashed mid-write leaving a partial proof; third-party scripts rewriting `fresh-layout-v1:` receipts without their payload.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- absent migration source has a digest
- InvalidData
- live principal migration receipt is missing
- principal migration component has no ordinary-home receipt
- required system migration receipt is missing
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/947aa0f715362cac.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-kernel/src/legacy_migration_barrier/proof.rs:71
|| value.starts_with("verified-capsule-authority-v1:")
|| value.starts_with("verified-system-env-v1:")
|| value.starts_with("verified-secret-import-v1:")
|| value.starts_with("fresh-layout-v1:");
if !known_prefix {
return Err("unknown destination proof prefix");
}
let rest = value
.split_once(':')
.map(|(_, rest)| rest)
.unwrap_or_default();
if rest.is_empty() {
return Err("destination proof is missing its payload");
}
Ok(Self(value))
}
pub(super) fn from_stored(value: impl Into<String>) -> io::Result<Self> {
Self::parse(value).map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))
}
pub(super) fn is_absent(&self) -> bool {
self.0 == Self::ABSENT
}
}
impl std::fmt::Display for DestinationProof {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(&self.0)
}
}
impl AsRef<str> for DestinationProof {
fn as_ref(&self) -> &str {
&self.0
}
}View on GitHub (pinned to affd8760f4)