astrid-runtime/astrid · error
signed Distro.lock entry '{}' has no capsule hash
Error message
signed Distro.lock entry '{}' has no capsule hash What it means
Every capsule entry in the signed Distro.lock must carry a content hash; the hash is what the trust layer pins and verifies after signature checking. An empty `hash` means the lock entry is unusable for integrity verification, so validate_signed_member_sets rejects it.
Source
Thrown at crates/astrid-cli/src/commands/init_signed_source.rs:366
);
for capsule in &lock.capsules {
let declared_capsule = declared
.get(capsule.name.as_str())
.copied()
.ok_or_else(|| {
anyhow::anyhow!(
"signed Distro.lock contains undeclared capsule '{}'",
capsule.name
)
})?;
if capsule.source != declared_capsule.source || capsule.version != declared_capsule.version
{
bail!(
"signed Distro.lock entry '{}' does not match Distro.toml",
capsule.name
);
}
anyhow::ensure!(
!capsule.hash.is_empty(),
"signed Distro.lock entry '{}' has no capsule hash",
capsule.name
);
}
Ok(())
}
View on GitHub (pinned to affd8760f4)
Solutions
- Regenerate Distro.lock with the official lock/seal command so each capsule gets a real content hash
- Fill in the correct hash for the entry (computed over the capsule artifact) and re-sign the lock
- Verify the lock file was not truncated or edited in transit (compare against the publisher's copy)
Example fix
# before [[capsules]] name = "alpha" hash = "" # after [[capsules]] name = "alpha" hash = "sha256:<hex-of-capsule>"
Defensive patterns
Strategy: validation
Validate before calling
for c in &lock.capsules {
anyhow::ensure!(!c.hash.is_empty(), "capsule '{}' in Distro.lock has no hash; regenerate the lock", c.name);
} Type guard
fn has_hash(c: &DistroLockCapsule) -> bool { !c.hash.is_empty() } Try / catch
match validate_signed_member_sets(manifest, lock) {
Err(e) if e.to_string().contains("has no capsule hash") => {
eprintln!("Lock entry missing hash — regenerate Distro.lock with the official seal step");
}
r => r?,
} Prevention
- Never author Distro.lock by hand; use the lock/seal command so hashes are computed
- Validate every lock entry has a non-empty hash before publishing
- Compare published lock against a freshly generated one in CI
When it happens
Trigger: fetch_signed_manifest -> verify_signed_manifest -> validate_signed_member_sets when a Distro.lock capsule entry has `hash = ""` or the hash field omitted — typically a hand-written or partially generated lock.
Common situations: Lock authored manually without running the hash step; a tool bug that emitted empty hashes; lock file truncated/edited during publish.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- opaque capsule asset directories cannot be symlinks: {}
- capsule archive already contains {PROVENANCE_FILE}
- capsule '{}' hash mismatch: signed lock has {}, resolved art
- InvalidData
- opaque asset path must be a directory: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/1b2a0b99eaa3b698.
Report an issue: GitHub.