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

  1. Regenerate Distro.lock with the official lock/seal command so each capsule gets a real content hash
  2. Fill in the correct hash for the entry (computed over the capsule artifact) and re-sign the lock
  3. 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

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


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/1b2a0b99eaa3b698. Report an issue: GitHub.