jdx/mise · error
remote action result is missing its output root
Error message
remote action result is missing its output root
What it means
HttpTaskCacheStore::get fetched a remote action result for a cache key, and the result declared output roots in its manifest but carried no output_root tree blob. The store refuses to fabricate an empty artifact when the manifest says outputs exist. This indicates a truncated, corrupt, or inconsistently written remote cache entry.
Solutions
- Re-run the task so it re-executes and republishes a complete remote cache entry with its output root.
- Purge the corrupt cache key from the remote cache server so get() returns a miss instead of an inconsistent entry.
- Verify the remote cache backend preserves tree blobs and writes the action result only after all blobs are uploaded.
- Check for a version-skewed client writing results in an incompatible format and align client versions.
Example fix
// Corrupt remote entry; force a fresh write $ mise run build --force # re-executes task and repopulates remote cache // or server-side $ curl -X DELETE https://cache.example.com/cas/<output-root-digest>
Defensive patterns
Strategy: fallback
Validate before calling
// Caller-side: treat fetch failure as a cache miss and rebuild
match store.get(&key, action_size).await {
Ok(Some(entry)) => entry,
_ => execute_task_and_republish().await?,
} Type guard
fn has_output_root(result: &RemoteActionResult, manifest_roots: &[PathBuf]) -> bool {
result.output_root.is_some() || manifest_roots.is_empty()
} Try / catch
let entry = match store.get(&key, size).await {
Ok(e) => e,
Err(e) if e.to_string().contains("missing its output root") => {
log::warn!("inconsistent remote entry for {key}; rebuilding");
None
}
Err(e) => return Err(e),
}; Prevention
- Ensure the remote cache server publishes the action result only after all tree blobs are stored.
- Treat remote-cache read errors as cache misses and rebuild rather than hard-failing.
- Monitor the cache server for deleted/truncated tree blobs.
- Keep all writers on the same client version.
When it happens
Trigger: Calling get() on the HTTP remote task cache store when the RemoteActionResult has output_root == None while the decoded manifest.roots is non-empty (task_cache_store.rs:418).
Common situations: A remote cache server (e.g. a CAS proxy) that drops or never stored the tree blob; a partially failed publish that wrote the action result before the output root; a misbehaving non-mise writer populating the remote cache; manual deletion of tree blobs server-side.
Related errors
- remote cache action bytes do not match cache key
- cached failed digest verification
- cached OCI layer diff ID mismatch
- cached OCI layer digest mismatch
- cached OCI layer size mismatch
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/1c22b18ce9ba745a.
Report an issue: GitHub.
Appendix: source
Thrown at src/task/task_cache_store.rs:418
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)?;
Ok(Some(TaskCacheStoreEntry { manifest, artifact }))
}
fn begin_write(&self, key: &str) -> Result<TaskCacheStoreWrite> {
validate_remote_key(key)?;
file::create_dir_all(&self.staging_dir)?;
let nonce = crate::rand::random_string(8);
Ok(TaskCacheStoreWrite {
artifact_path: self.staging_dir.join(format!("{key}.part-{nonce}.tar.zst")),
manifest_path: self.staging_dir.join(format!("{key}.part-{nonce}.json")),
})
}View on GitHub (pinned to 533346cc37)