jdx/mise · error · eyre::Report
local action result is invalid
Error message
local action result is invalid: {} What it means
`LocalActionCache::find` reads the stored JSON for an action digest and strictly validates it: `version` must be 1, the embedded `action` digest must equal the requested key, and the raw file bytes must equal the canonical re-serialization of the parsed result. Any deviation - hand edits, truncation, or a different serialization - makes the entry invalid and the error names the file. The store path self-heals: `store` detects an invalid existing entry and atomically replaces it.
Solutions
- Re-store to heal: `LocalActionCache::store` replaces invalid entries via its `replace_invalid` path.
- Or delete the named JSON file (or the `action-results/v1` subtree) - the next successful build republishes it.
- Do not edit files under the cache root; give each tool version its own cache directory.
Example fix
// before: the invalid entry propagates as an error
let result = actions.find(&action)?;
// after: treat as a miss, then republish to heal
let result = match actions.find(&action) {
Ok(result) => result,
Err(_) => None, // recompute
};
let fresh = recompute_result();
actions.store(&fresh)?; // atomically replaces the invalid entry Defensive patterns
Strategy: fallback
Try / catch
let result = match actions.find(&action) {
Ok(result) => result,
Err(err) => {
warn!("invalid action result, treating as miss: {err}");
None
}
};
// recompute on None, then actions.store(&fresh) heals the invalid entry atomically Prevention
- Treat action-cache find errors as misses and let store() replace invalid entries.
- Never hand-edit JSON under action-results/v1.
- Give each tool version its own cache root to avoid canonical-JSON mismatches.
When it happens
Trigger: Reading an action-result JSON that changed after publishing: manual edits, a partial write from a crash in a pre-atomic version, tools that reformat the cache, or a cache directory shared with another version whose canonical JSON differs.
Common situations: Users inspecting or pretty-printing cache files; mixed-version cache directories; disk-cleanup tools truncating files.
Related errors
- cannot publish an action result with a missing blob
- local action key already has a different result
- local action keys must use blake3
- unsupported local action result version
- bytes do not match the declared CAS digest
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/e7a8af3cb1e7554c.
Report an issue: GitHub.
Appendix: 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 6f52dcdf99)