jdx/mise · error · eyre::Report
remote action manifest ETag does not match its body
Error message
remote action manifest ETag does not match its body
What it means
get_action_manifest requires the response to carry a strong quoted ETag equal to the blake3 hex digest of the exact body bytes. After downloading, the client recomputes blake3 over the body and compares; a mismatch means the body or the ETag was altered between server and client, or the server does not implement the contract. This check protects the optimistic-concurrency (If-Match) manifest updates from silent corruption.
Source
Thrown at crates/mise-cache-core/src/lib.rs:425
let url = self.action_manifest_endpoint(key)?;
retry_async("GET", &url, self.retries, || async {
let response = self
.request(
reqwest::Method::GET,
url.clone(),
TASK_ACTION_MANIFEST_MEDIA_TYPE,
)
.await?
.send()
.await?;
if response.status() == StatusCode::NOT_FOUND {
return Ok(None);
}
let response = response.error_for_status()?;
let etag = parse_strong_etag(response.headers().get(ETAG))?;
let bytes = response.bytes().await?.to_vec();
if blake3::hash(&bytes).to_hex().as_str() != etag {
bail!("remote action manifest ETag does not match its body");
}
Ok(Some(RemoteActionManifest { bytes, etag }))
})
.await
}
pub async fn put_action_manifest(
&self,
key: &CacheDigest,
bytes: &[u8],
expected_etag: Option<&str>,
) -> Result<ManifestPutOutcome> {
let url = self.action_manifest_endpoint(key)?;
let body = bytes.to_vec();
let expected_etag = expected_etag.map(quoted_etag).transpose()?;
retry_async("PUT", &url, self.retries, || async {
let mut request = self
.request(View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Disable body transformation (gzip/transcoding) for the manifest media type on any proxy or CDN in front of the cache
- Fix the server to set the ETag to the quoted blake3 hex digest of the exact bytes it serves
- Purge the affected manifest entries on the server/CDN once corruption is confirmed
- Treat a manifest fetch that fails this check as absent (None) and rebuild the manifest from local state
Example fix
# before: proxy rewrites the manifest body (gzip) but forwards the original ETag
location /v1/action-manifests/ {
gzip on; # body no longer hashes to the ETag -> client rejects it
}
# after: serve manifest bytes untransformed so they hash to their ETag
location /v1/action-manifests/ {
gzip off;
proxy_set_header Accept-Encoding "";
} Defensive patterns
Strategy: fallback
Try / catch
let manifest = match client.get_action_manifest(&key).await {
Ok(manifest) => manifest,
Err(report) if report.to_string().contains("ETag does not match its body") => {
// body/etag corruption in transit: treat as absent and rebuild the manifest
None
}
Err(report) => return Err(report),
}; Prevention
- Disable body-transforming proxies (gzip, re-encoding) for the manifest media type
- If you implement a server, always compute the strong ETag as quoted blake3 hex of the served bytes
- Purge CDN entries for manifest paths when deploying server changes
When it happens
Trigger: An intermediary proxy that transcodes the body (gzip, charset re-encoding) while forwarding the original ETag; a server that computes the ETag with sha256 or over different bytes; a CDN serving a stale body with a newer ETag; deliberate cache poisoning.
Common situations: Corporate proxies or CDNs modifying response bodies; a custom cache server implemented against an older or incorrect spec; compression enabled at the proxy without recomputing ETags for the application/vnd.mise.cache-task-action-manifest.v1+json media type.
Related errors
- remote action manifest keys must use blake3
- invalid remote action manifest ETag
- remote cache action keys must use blake3
- remote action result does not match requested action
- remote cache blob failed digest verification
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/3434eb3c06a8a42e.
Report an issue: GitHub.