jdx/mise · error · eyre::Report
cached {description} is not canonical JSON
Error message
cached {description} is not canonical JSON What it means
read_canonical_blob() enforces structural integrity on cached JSON blobs: it parses the bytes, re-serializes with canonical_json, and requires byte equality (src/cache/rustc.rs:516-521). The blob passed the blake3 digest check but is not the canonical encoding, so it was produced by a different serializer version or otherwise diverges from what this build would have written; the entry is treated as untrusted and rejected. It applies to the RustcMetadata and CacheDirectory descriptors read during restore_result (src/cache/rustc.rs:310-316).
Source
Thrown at src/cache/rustc.rs:520
AgentResponse::Blob { path: Some(path) } if digests.len() == 1 => Ok(vec![path]),
AgentResponse::Blob { path: None } if digests.len() == 1 => {
let digest = &digests[0];
bail!("cached rustc action is missing blob {}", digest.hash)
}
AgentResponse::Error { message } => bail!(message),
_ => bail!("cache agent returned an unexpected blob lookup response"),
}
}
fn read_canonical_blob<T>(path: &Path, digest: &CacheDigest, description: &str) -> Result<T>
where
T: DeserializeOwned + Serialize,
{
let bytes = read_verified_blob(path, digest, description)?;
let value = serde_json::from_slice(&bytes)
.wrap_err_with(|| format!("cached {description} is not valid JSON"))?;
if canonical_json(&value)? != bytes {
bail!("cached {description} is not canonical JSON");
}
Ok(value)
}
fn read_verified_blob(path: &Path, digest: &CacheDigest, description: &str) -> Result<Vec<u8>> {
let mut bytes = Vec::new();
std::fs::File::open(path)?.read_to_end(&mut bytes)?;
if !digest.matches_bytes(&bytes)? {
bail!("cached {description} failed digest verification");
}
Ok(bytes)
}
fn validated_outputs(
directory: CacheDirectory,
outputs: &RustcOutputs,
) -> Result<Vec<(CacheFileNode, PathBuf)>> {
if directory.version != 1 || !directory.directories.is_empty() || !directory.symlinks.is_empty()View on GitHub (pinned to 6f52dcdf99)
Solutions
- Clear the affected cache (local directory and, if you control it, the remote namespace) and let it repopulate from a single mise version
- Pin one mise version per shared cache/namespace, or version the namespace string with the mise version
- If it appears without any version change, treat it as corruption: check disk health and who else writes into the cache dir
Defensive patterns
Strategy: fallback
Validate before calling
fn is_canonical_cached_json(path: &Path, digest: &CacheDigest) -> bool {
let Ok(bytes) = std::fs::read(path) else { return false };
let Ok(value) = serde_json::from_slice::<serde_json::Value>(&bytes) else { return false };
digest.matches_bytes(&bytes).unwrap_or(false)
&& matches!(canonical_json(&value), Ok(enc) if enc == bytes)
} Prevention
- Version remote cache namespaces with the mise version (e.g. 'ci-2026-1') so serializer changes never mix
- Never share a local action-cache dir between users or mise versions
- Clear caches wholesale after mise upgrades that touch the cache protocol
When it happens
Trigger: Restoring a metadata or output-directory blob written by a mise version whose canonical JSON encoding (field order, escaping, number formatting) differs; a remote cache namespace populated by clients on a different mise version; cache files rewritten in place by external tooling while the digest list still matches by coincidence of write-order.
Common situations: Sharing one cache directory or remote namespace across mise upgrades; CI caches populated by an older image; hand-edited cache files.
Related errors
- cache agent returned an unexpected blob lookup response
- cached {description} failed digest verification
- remote cache blob failed digest verification
- local action result is invalid: {}
- cached rustc action is missing blob {}
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/d81a448bc6ac08b5.
Report an issue: GitHub.