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

  1. Restore the full proof string including its payload (e.g. `verified-empty-v1:<digest-or-id>`) in the migration ledger.
  2. Regenerate the ledger by re-running the migration/verification step that produced the destination proof.
  3. If the ledger is unrecoverable, remove it and re-run the component import so fresh proofs are written.
  4. 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

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


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)