{"record":{"id":"8bfe6b5aef3463d2","repo":"jdx/mise","slug":"task-cache-outputs-must-not-contain-source","errorCode":null,"errorMessage":"task {} cache outputs must not contain source {}","messagePattern":"task (.+?) cache outputs must not contain source (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/task_cache.rs","lineNumber":283,"sourceCode":"        let root = task_cwd(task, config).await?;\n        validate_config(task, &root)?;\n        let output_roots = resolve_output_roots(task, &root, false)?;\n        for output in &output_roots {\n            ensure_no_symlink_ancestors(&root, output)?;\n        }\n        let Some(inputs) = task_cache_inputs(task, config, !dry_run).await? else {\n            warn!(\n                \"task {} has sources defined but no matching files found; artifact caching disabled\",\n                task.name\n            );\n            return Ok(None);\n        };\n        for source in &inputs.source_paths {\n            if output_roots\n                .iter()\n                .any(|output| source == output || source.starts_with(output))\n            {\n                bail!(\n                    \"task {} cache outputs must not contain source {}\",\n                    task.name,\n                    source.display()\n                );\n            }\n        }\n        Ok(Some(TaskArtifactCacheBuilder {\n            root,\n            inputs,\n            output_roots,\n        }))\n    }\n}\n\nimpl TaskArtifactCacheBuilder {\n    /// Finishes cache-key construction after task tools, environment, and\n    /// dependency artifacts have been resolved.\n    pub(crate) async fn finish(self, ctx: TaskCacheContext<'_>) -> Result<TaskArtifactCache> {","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/task/task_cache.rs#L265-L301","documentation":"While building the task artifact cache, mise resolves the task's output roots and input source paths, then verifies no source equals or lies beneath an output root. Overlap would make the cache feed its own restored outputs back in as inputs (and rewrite the cache key every run), so the builder bails.","triggerScenarios":"A cached task with sources = [\"src/**\"] and outputs = [\"src/**\"], or outputs = [\".\"] / outputs = [\"\"] whose resolved root contains every source path; the check runs in TaskArtifactCacheBuilder construction after task_cache_inputs returns concrete source_paths.","commonSituations":"In-place codegen tasks that rewrite their inputs; broad outputs like '.' used to cache 'anything that changed'; tasks that emit next to sources (e.g. *.ts -> *.js in the same tree) without excluding the sources.","solutions":["Point outputs at a separate directory from sources (e.g. outputs = [\"dist/**\"])","If the task has no meaningful files to cache, declare outputs = [] and rely on command_inputs for keying","Narrow sources or outputs so no output root is a prefix of any source path","For in-place transforms, disable artifact caching and use run = \"always\" semantics instead"],"exampleFix":"# before\n[tasks.codegen]\nsources = [\"src/schema/**\"]\noutputs = [\"src/**\"]\n\n# after\n[tasks.codegen]\nsources = [\"src/schema/**\"]\noutputs = [\"src/generated/**\"]","handlingStrategy":"validation","validationCode":"# rough overlap check before enabling cache\npython3 - <<'EOF'\nimport tomllib, sys\ncfg = tomllib.load(open('mise.toml','rb'))\nfor name, t in cfg.get('tasks', {}).items():\n    if not t.get('cache'): continue\n    for s in t.get('sources', []):\n        base = s.rstrip('*').rstrip('/')\n        for o in t.get('outputs', []):\n            ob = o.rstrip('*').rstrip('/')\n            if base and ob and (base.startswith(ob) or ob.startswith(base)):\n                sys.exit(f\"{name}: output '{o}' overlaps source '{s}'\")\nprint('ok')\nEOF","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep sources and outputs in disjoint directories","Prefer writing build artifacts to a dedicated dir (dist/, build/, target/)","Use outputs = [] when there is nothing to restore"],"tags":["mise","task-cache","caching","configuration","artifacts"],"backgroundTag":"cache-output-overlaps-source","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}