{"record":{"id":"606c96a4b71e7b67","repo":"jdx/mise","slug":"remote-action-output-tree-is-too-large","errorCode":null,"errorMessage":"remote action output tree is too large","messagePattern":"remote action output tree is too large","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/agent.rs","lineNumber":1253,"sourceCode":"                    }\n                }\n            } else if !verified.contains_key(metadata) {\n                bail!(\"remote action metadata is missing\");\n            }\n        }\n        let mut pending = action\n            .result\n            .output_root\n            .iter()\n            .cloned()\n            .collect::<Vec<_>>();\n        let mut seen = BTreeMap::new();\n        while let Some(digest) = pending.pop() {\n            if seen.insert(digest.clone(), ()).is_some() {\n                continue;\n            }\n            if seen.len() > MAX_PREFETCH_DIRECTORY_OBJECTS {\n                bail!(\"remote action output tree is too large\");\n            }\n            let directory = directories\n                .get(&digest)\n                .ok_or_else(|| eyre::eyre!(\"remote action output directory is missing\"))?;\n            for file in &directory.files {\n                if !verified.contains_key(&file.digest) {\n                    bail!(\"remote action output file is missing\");\n                }\n            }\n            pending.extend(\n                directory\n                    .directories\n                    .iter()\n                    .map(|directory| directory.digest.clone()),\n            );\n        }\n        Ok(())\n    }","sourceCodeStart":1235,"sourceCodeEnd":1271,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/crates/mise-cache-core/src/agent.rs#L1235-L1271","documentation":"validate_prefetched_action walks the output_root directory DAG (BFS over directories), dedupes digests via a `seen` map, and aborts once it exceeds MAX_PREFETCH_DIRECTORY_OBJECTS (100,000). The bound protects the agent from unbounded memory/work when a cached output tree is enormous.","triggerScenarios":"A cached action whose output tree contains more than 100,000 distinct directory objects — e.g. full build trees, vendored dependencies inside outputs, or an accidental cache of a huge directory.","commonSituations":"Caching actions with massive/recursive outputs; a task bug writing generated trees into the cached output root; pathological or hostile cache entries designed to blow up traversal.","solutions":["Shrink the action's cached outputs or exclude the oversized tree from caching","Split the action so each cached result stays under 100k directory objects","If unexpected, inspect what the action actually wrote into its output root — usually a bug generating a huge tree"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before storing/prefetching, bound the output tree yourself\nconst MAX_DIR_OBJECTS: usize = 100_000;\nfn count_directory_objects(root: &Path) -> std::io::Result<usize> {\n    let mut n = 0;\n    for entry in walkdir::WalkDir::new(root) { entry?; n += 1; }\n    Ok(n)\n}\nassert!(count_directory_objects(&output_root)? <= MAX_DIR_OBJECTS);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep cached output roots small and focused; move bulky generated trees outside the cached output","Audit actions that write into output roots recursively — runaway generation hits this cap","Remember cycles are deduped but sheer object count is not; 100,001 distinct directories trips it"],"tags":["mise-cache","prefetch","resource-limit","output-tree"],"backgroundTag":"cache-entry-too-large","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}