jdx/mise · error
remote cache directory graph contains a cycle
Error message
remote cache directory graph contains a cycle
What it means
When materializing a remote cache directory tree, the walker tracks the set of digests on the current path (ancestors). If a directory blob's digest is already among its own ancestors, the remote CAS graph is cyclic and would recurse forever, so mise bails. This protects against corrupt or malicious remote data.
Solutions
- Delete the affected cache entry from the remote store and re-run the task to repopulate it.
- Verify the remote store's digests against content (each digest must be the hash of its blob) and purge mismatches.
- Audit any custom cache-writing tooling for digest computation or deduplication bugs.
- Fall back to running the task with cache disabled to unblock local work while the remote entry is fixed.
Defensive patterns
Strategy: validation
Validate before calling
function hasCycle(dir, ancestors = new Set()) {
if (ancestors.has(dir.digest)) return true;
ancestors.add(dir.digest);
return dir.childDirs.some(c => hasCycle(c, new Set(ancestors)));
} Prevention
- Never hand-edit remote cache blobs.
- Ensure digests are always the hash of the exact blob content.
- Periodically audit the remote store for digest/content mismatches.
When it happens
Trigger: A remote directory blob references a child directory whose digest equals an ancestor already on the traversal stack — only possible with a corrupted, hand-edited, or maliciously crafted remote cache entry set.
Common situations: Manual tampering with blobs in the remote object store; a buggy custom cache server that computed/reused digests incorrectly; store corruption from partial writes or blob aliasing.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- invalid remote task cache key
- remote cache directory contains duplicate names
- remote cache directory is not canonical JSON
- unsupported remote cache directory version
- unsupported task cache archive entry type
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/f4d459e788c85c60.
Report an issue: GitHub.
Appendix: source
Thrown at src/task/task_cache_store.rs:715
digest: CacheDigest,
executable: bool,
mode: u32,
},
Symlink {
mode: u32,
target: PathBuf,
},
}
async fn materialize_remote_tree(
store: &HttpTaskCacheStore,
root: &CacheDigest,
) -> Result<tempfile::NamedTempFile> {
let mut pending = vec![(PathBuf::new(), root.clone(), BTreeSet::new())];
let mut nodes = BTreeMap::<PathBuf, RestoredNode>::new();
while let Some((path, digest, mut ancestors)) = pending.pop() {
if !ancestors.insert(digest.clone()) {
bail!("remote cache directory graph contains a cycle");
}
let bytes = store.client.get_blob(&digest, DIRECTORY_MEDIA_TYPE).await?;
let directory: RemoteDirectory = serde_json::from_slice(&bytes)?;
if canonical_json(&serde_json::to_value(&directory)?)? != bytes {
bail!("remote cache directory is not canonical JSON");
}
if directory.version != 1 {
bail!("unsupported remote cache directory version");
}
let mut names = BTreeSet::new();
for directory in directory.directories {
validate_cache_name(&directory.name)?;
if !names.insert(directory.name.clone()) {
bail!("remote cache directory contains duplicate names");
}
let child = path.join(&directory.name);
validate_cache_path(&child)?;
nodes.insert(View on GitHub (pinned to 533346cc37)