jdx/mise · error · eyre::Report

local action keys must use blake3

Error message

local action keys must use blake3

What it means

LocalActionCache::path_for — and therefore find/store — requires action digests to use blake3, mirroring the remote client's rule for action keys. Blob digests in the CAS may use sha256, but the local action-result index (action-results/v1/blake3/<hash[0..2]>/<hash>-<size>.json) only accepts blake3 keys.

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 9dcfcaa0dc)

Solutions

  1. Build action keys with CacheDigest::blake3(&canonical_json(&result)?)
  2. Keep one dedicated code path for action keys (blake3) even when blob digests use sha256
  3. Assert action.algorithm == "blake3" before calling find/store so misuse fails at the call site

Example fix

// before
let action = CacheDigest { algorithm: "sha256".into(), .. };
actions.find(&action)?;

// after
let action = CacheDigest::blake3(&canonical_json(&result)?);
actions.find(&action)?;
Defensive patterns

Strategy: validation

Validate before calling

fn local_action_key(result: &RemoteActionResult) -> eyre::Result<CacheDigest> {
    let bytes = mise_cache_core::canonical_json(result)?;
    Ok(CacheDigest::blake3(&bytes)) // local action keys are always blake3
}

Type guard

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

Prevention

When it happens

Trigger: Passing a sha256 CacheDigest to LocalActionCache::find or store; reusing a blob digest as an action key; porting an integration that indexed action results by sha256 before this crate pinned blake3.

Common situations: Digest plumbing where blob digests (any supported algorithm) and action keys (blake3 only) flow through the same helper; upgrades from earlier prototypes; tests sharing fixture digests between blobs and actions.

Related errors


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