jdx/mise · error
remote cache client metadata is not canonical JSON
Error message
remote cache client metadata is not canonical JSON
What it means
Remote client metadata blobs must be stored as canonical JSON (deterministic key order/encoding). After deserializing, get() re-serializes the metadata canonically and compares bytes; any difference means the blob wasn't written in canonical form, so mise rejects it to guarantee content addressing integrity.
Solutions
- Upload the metadata blob exactly as mise produced it (raw bytes, no re-encoding)
- Delete the non-canonical remote entry and let mise republish it by re-running the task
- Bypass any proxy/middleware that rewrites response bodies for the cache storage
Defensive patterns
Strategy: validation
Validate before calling
# verify uploaded blob is byte-identical to what mise wrote cmp mise_metadata.json uploaded_blob.json
Try / catch
match cache_get(key) {
Err(e) if e.to_string().contains("canonical JSON") => {
// republish: delete blob and rerun the task
purge_key(key);
rerun_task();
}
r => r?,
} Prevention
- Upload cache blobs verbatim; never round-trip them through a JSON re-encoder
- Disable body-transforming proxies/CDN features on cache storage
- Only let mise itself write remote cache metadata
When it happens
Trigger: Reading a remote cache entry where the metadata blob bytes differ from the canonical re-encoding of their parsed content — e.g. metadata uploaded by non-mise tooling, pretty-printed JSON, or re-encoded by a proxy/CDN transformation.
Common situations: Custom cache upload scripts writing JSON via a generic encoder; intermediaries that reformat or recompress blobs; hand-crafted cache entries in a shared bucket.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- remote cache directory is not canonical JSON
- cached is not canonical JSON
- cached rustc output directory has unsupported entries
- cannot establish pitchfork supervisor status
- deps output rules should serialize
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/359c6fd10a752e46.
Report an issue: GitHub.
Appendix: source
Thrown at src/task/task_cache_store.rs:406
let action = CacheDigest {
algorithm: "blake3".into(),
hash: key.to_string(),
size: action_size,
};
let Some(result) = self.client.get_action_result(&action).await? else {
return Ok(None);
};
let metadata = result
.metadata
.as_ref()
.ok_or_else(|| eyre!("remote action result is missing client metadata"))?;
let metadata_bytes = self
.client
.get_blob(metadata, CLIENT_METADATA_MEDIA_TYPE)
.await?;
let metadata: RemoteClientMetadata = serde_json::from_slice(&metadata_bytes)?;
if canonical_json(&serde_json::to_value(&metadata)?)? != metadata_bytes {
bail!("remote cache client metadata is not canonical JSON");
}
let mut manifest = metadata.into_manifest(key)?;
for root in &manifest.roots {
validate_cache_path(root)?;
}
let artifact = match &result.output_root {
Some(root) => {
let temporary = materialize_remote_tree(self, root).await?;
Some(TaskCacheStoreArtifact::temporary(temporary))
}
None if manifest.roots.is_empty() => None,
None => bail!("remote action result is missing its output root"),
};
manifest.artifact_checksum = Some(calculate_artifact_checksum(
&manifest,
artifact.as_ref().map(TaskCacheStoreArtifact::path),
)?);
let manifest = serde_json::to_vec(&manifest)?;View on GitHub (pinned to 533346cc37)