jdx/mise · error

local action keys must use blake3

Error message

local action keys must use blake3

What it means

`LocalActionCache::path_for` resolves the storage path for an action digest and requires the digest's algorithm to be exactly `blake3`. Sha-256 digests are valid for CAS blobs, but the action-result index is keyed by blake3 only, so any other algorithm is rejected before filesystem access. Every `find`/`store` call goes through `path_for`, so this guard covers the whole action cache API.

Solutions

  1. Compute action keys with `CacheDigest::blake3(bytes)` or `CacheDigest::blake3_file(&path)`.
  2. When a digest arrives from external data, check `digest.algorithm == "blake3"` before using it as an action key.
  3. Reserve sha-256 digests for blob storage only.

Example fix

// before: sha256 action key - path_for bails
let action = CacheDigest { algorithm: "sha256".into(), hash: sha256_hex, size };
actions.find(&action)?;

// after: action keys are blake3
let action = CacheDigest::blake3(&manifest_bytes);
actions.find(&action)?;
Defensive patterns

Strategy: type-guard

Type guard

fn is_action_key(digest: &CacheDigest) -> bool {
    digest.algorithm == "blake3" && digest.validate().is_ok()
}

// usage
if is_action_key(&digest) {
    let found = actions.find(&digest)?;
} else {
    // sha256 digests are blob keys only: store via LocalCas, never LocalActionCache
}

Prevention

When it happens

Trigger: Constructing a `CacheDigest` with `algorithm: "sha256"` (or any non-blake3 value) and passing it to `LocalActionCache::find`/`store`/`path_for`; deserializing an action digest from remote or JSON data that used sha256; reusing blob digests as action keys.

Common situations: Interop code hashing action manifests with sha-256 out of habit; mixing digest constructors between the blob CAS (blake3 or sha256) and the action cache (blake3 only); digests arriving from external systems.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/79a4b0b3186b3728. Report an issue: GitHub.

Appendix: source

Thrown at crates/mise-cache-core/src/local.rs:177

    fs::set_permissions(path, permissions)?;
    Ok(())
}

impl LocalActionCache {
    /// Create an action-result index beneath `root`.
    pub fn new(root: impl Into<PathBuf>) -> Self {
        let root = root.into();
        Self {
            cas: LocalCas::new(root.clone()),
            root,
        }
    }

    /// Resolve the storage path for an action digest.
    pub fn path_for(&self, action: &CacheDigest) -> Result<PathBuf> {
        action.validate()?;
        if action.algorithm != "blake3" {
            bail!("local action keys must use blake3");
        }
        Ok(self
            .root
            .join("action-results/v1")
            .join(&action.algorithm)
            .join(&action.hash[..2])
            .join(format!("{}-{}.json", action.hash, action.size)))
    }

    /// Find and strictly validate a canonical action result.
    pub fn find(&self, action: &CacheDigest) -> Result<Option<RemoteActionResult>> {
        let path = self.path_for(action)?;
        if !path.exists() {
            return Ok(None);
        }
        let bytes = fs::read(&path)?;
        let result: RemoteActionResult = serde_json::from_slice(&bytes)?;
        if result.version != 1 || result.action != *action || canonical_json(&result)? != bytes {

View on GitHub (pinned to 6f52dcdf99)