jdx/mise · error
remote cache client metadata is not canonical JSON
Error message
remote cache client metadata is not canonical JSON
What it means
After fetching the client-metadata blob, mise re-serializes the parsed value with its canonical JSON encoder and requires the output to equal the served bytes byte-for-byte (src/task/task_cache_store.rs:405-407). Any difference — key reordering, whitespace, escape style, unicode normalization — means the blob was not stored or served verbatim, which breaks the content-addressed trust model, so the read aborts.
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 6f52dcdf99)
Solutions
- Verify the server serves blob bytes unmodified: compare the blake3 digest of what the client uploaded with what a direct GET returns.
- Remove any JSON-transforming proxy/CDN rule (disable content optimization) in front of the remote cache URL, or bypass it.
- Delete the non-canonical entries and let a current mise rewrite them canonically.
- Until fixed, set task.cache.remote_mode (or MISE_TASK_CACHE_REMOTE_MODE) to write-only so reads never hit the rewriting layer.
Example fix
# before: server re-emits JSON from a DB row (non-canonical)
GET /blobs/<digest> -> {"version": 1, "kind": "task", ...} # reordered keys
# after: server stores and returns the exact uploaded bytes
GET /blobs/<digest> -> exact byte stream, digest matches blake3 of upload Defensive patterns
Strategy: fallback
Validate before calling
# verify the server serves blobs byte-exact: fetch a blob twice and hash it curl -s "$REMOTE_URL/blobs/$DIGEST" | b3sum # must equal the digest it was stored under curl -s "$REMOTE_URL/blobs/$DIGEST" | b3sum # stable across reads
Try / catch
# shell: if a rewriting proxy corrupts blobs, drop to write-only and report
if ! mise run build; then
dmesg_cache_err=$(mise run build 2>&1)
case "$dmesg_cache_err" in *"not canonical JSON"*)
echo "remote cache server rewrites JSON — disabling remote reads";
MISE_TASK_CACHE_REMOTE_MODE=write-only mise run build ;;
esac
fi Prevention
- Serve cache blobs from immutable, byte-exact storage; never regenerate JSON on read.
- Disable JSON minification/optimization on any proxy, CDN, or WAF in front of the cache URL.
- Smoke-test the endpoint: GET a known blob and compare its hash to the digest used in the URL.
- Keep remote reads off (write-only) until byte-exactness is proven after infrastructure changes.
When it happens
Trigger: get_action_result returns a metadata digest; get_blob fetches it; canonical_json(serde_json::to_value(metadata)) != metadata_bytes. Happens when a proxy, CDN, or gateway re-serializes JSON responses, when the server regenerates JSON on read instead of serving the stored blob, or when a non-mise writer uploaded pretty-printed/non-canonical JSON.
Common situations: Corporate MITM proxies or CDNs configured to "optimize" JSON; a custom remote-cache server that stores metadata as a database row and re-emits JSON; hand-uploaded debug blobs; charset/encoding transcoding in front of the cache endpoint.
Related errors
- remote cache directory is not canonical JSON
- task action manifest is not canonical JSON
- remote cache blob pack failed digest verification
- task cache artifact checksum mismatch
- unsupported remote cache client metadata version
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/359c6fd10a752e46.
Report an issue: GitHub.