astrid-runtime/astrid · error

Distro.lock capsule '{capsule}' requires a canonical blake3:

Error message

Distro.lock capsule '{capsule}' requires a canonical blake3:<hex> WASM hash

What it means

Distro.lock WASM hashes must use the canonical 'blake3:<64 hex chars>' form. parse_locked_blake3 rejects any value without the blake3: prefix, since non-prefixed or foreign-algorithm hashes cannot be verified against the content store.

Source

Thrown at crates/astrid-cli/src/commands/init_grant.rs:426

        let blob_path = home.bin_dir().join(format!("{locked_hex}.wasm"));
        std::fs::read(&blob_path).with_context(|| {
            format!(
                "Distro.lock capsule '{}' content blob is missing or unreadable at {}",
                capsule,
                blob_path.display()
            )
        })?
    };
    let actual = blake3::hash(&bytes);
    if actual != locked {
        bail!("Distro.lock capsule '{capsule}' content blob bytes do not match hash {locked_hash}");
    }
    Ok(())
}

fn parse_locked_blake3(capsule: &CapsuleId, value: &str) -> anyhow::Result<blake3::Hash> {
    let Some(hex) = value.strip_prefix("blake3:") else {
        bail!("Distro.lock capsule '{capsule}' requires a canonical blake3:<hex> WASM hash");
    };
    let hash = blake3::Hash::from_hex(hex).map_err(|_| {
        anyhow::anyhow!("Distro.lock capsule '{capsule}' has an invalid BLAKE3 hash")
    })?;
    if hex.len() != 64 || hash.to_hex().as_str() != hex {
        bail!("Distro.lock capsule '{capsule}' requires a canonical lowercase BLAKE3 hash");
    }
    Ok(hash)
}

fn manifest_declares_wasm(manifest: &CapsuleManifest) -> bool {
    manifest
        .components
        .iter()
        .any(|component| component.path.extension().and_then(|ext| ext.to_str()) == Some("wasm"))
}

/// Apply capsule-access grants for the installed set (opt-in), or print

View on GitHub (pinned to affd8760f4)

Solutions

  1. Rewrite the Distro.lock hash as blake3:<64-char lowercase hex>.
  2. Regenerate Distro.lock with the current CLI so hashes are emitted canonically.
  3. Recompute the artifact hash with blake3 (not sha256/xxhash) and store it with the prefix.

Example fix

// before (Distro.lock)
hash = "9f86d081884c7d659a2f..."
// after
hash = "blake3:9f86d081884c7d659a2f..."
Defensive patterns

Strategy: validation

Validate before calling

if !locked_hash.starts_with("blake3:") {
    return Err(anyhow!("hash must be blake3:<hex>"));
}

Type guard

fn is_blake3_lock_hash(v: &str) -> bool {
    v.strip_prefix("blake3:")
        .map(|h| h.len() == 64 && h.bytes().all(|b| b.is_ascii_hexdigit()))
        .unwrap_or(false)
}

Try / catch

match parse_locked_blake3(capsule, value) {
    Err(e) if e.to_string().contains("canonical blake3:") => fix_hash_prefix(capsule)?,
    other => other?,
}

Prevention

When it happens

Trigger: parse_locked_blake3 (called from validate_locked_wasm) receives a locked_hash where strip_prefix("blake3:") returns None — e.g. a bare hex digest, a sha256: prefix, or an empty string.

Common situations: Hand-edited lockfile dropping the prefix, lockfile written by an older tool using a different hash format, copy-pasting a digest from a non-blake3 tool.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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