jdx/mise · error · eyre::Report

local action result is invalid: {}

Error message

local action result is invalid: {}

What it means

LocalActionCache::find strictly validates a stored action-result file: version must equal 1, the record's action field must equal the requested digest, and the file bytes must exactly equal canonical_json of the parsed record. Any deviation — truncation, pretty-printing, a stale format, or a record stored under the wrong key — is treated as corruption and the offending path is included in the message.

Source

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

        }
        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 {
            bail!("local action result is invalid: {}", path.display());
        }
        Ok(Some(result))
    }

    /// Atomically publish an action result after validating all referenced objects.
    pub fn store(&self, result: &RemoteActionResult) -> Result<PathBuf> {
        if result.version != 1 {
            bail!("unsupported local action result version");
        }
        for digest in [
            Some(&result.action),
            result.metadata.as_ref(),
            result.output_root.as_ref(),
        ]
        .into_iter()
        .flatten()
        {
            if self.cas.find(digest)?.is_none() {

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Delete the offending .json file named in the error — LocalActionCache::store automatically replaces invalid entries on the next publish
  2. If many entries fail, clear the action-results directory (or the whole cache root) and repopulate from rebuilds
  3. Keep editors, formatters, and sync tools away from the cache root
  4. After upgrades, treat the old cache as disposable instead of migrating files by hand

Example fix

# before: actions.find(&action) fails with
#   local action result is invalid: /home/u/.cache/mise/action-results/v1/blake3/ab/abcd1234…-57.json

# after: remove the corrupt record (store() rewrites it on the next publish)
rm /home/u/.cache/mise/action-results/v1/blake3/ab/abcd1234…-57.json
Defensive patterns

Strategy: fallback

Try / catch

let result = match actions.find(&action) {
    Ok(result) => result,
    Err(report) if report.to_string().contains("local action result is invalid") => {
        // corrupt record: delete it, treat as a miss; store() rewrites it on next publish
        let _ = std::fs::remove_file(actions.path_for(&action)?);
        None
    }
    Err(report) => return Err(report),
};

Prevention

When it happens

Trigger: A JSON file under action-results/v1/ that was hand-edited or reformatted (breaking canonical byte equality); truncation from a crash in an older non-atomic writer; files written by a previous version with different canonicalization or version; a result written under another action's key.

Common situations: Developers or formatters 'normalizing' JSON inside the cache directory; power loss during writes before atomic persist existed; upgrading mise/cache tooling across format versions; tools that reserialize JSON on save.

Related errors


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