jdx/mise · error
task {} cache outputs must not contain source {}
Error message
task {} cache outputs must not contain source {} What it means
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.
Source
Thrown at src/task/task_cache.rs:283
let root = task_cwd(task, config).await?;
validate_config(task, &root)?;
let output_roots = resolve_output_roots(task, &root, false)?;
for output in &output_roots {
ensure_no_symlink_ancestors(&root, output)?;
}
let Some(inputs) = task_cache_inputs(task, config, !dry_run).await? else {
warn!(
"task {} has sources defined but no matching files found; artifact caching disabled",
task.name
);
return Ok(None);
};
for source in &inputs.source_paths {
if output_roots
.iter()
.any(|output| source == output || source.starts_with(output))
{
bail!(
"task {} cache outputs must not contain source {}",
task.name,
source.display()
);
}
}
Ok(Some(TaskArtifactCacheBuilder {
root,
inputs,
output_roots,
}))
}
}
impl TaskArtifactCacheBuilder {
/// Finishes cache-key construction after task tools, environment, and
/// dependency artifacts have been resolved.
pub(crate) async fn finish(self, ctx: TaskCacheContext<'_>) -> Result<TaskArtifactCache> {View on GitHub (pinned to 6f52dcdf99)
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
Example fix
# before [tasks.codegen] sources = ["src/schema/**"] outputs = ["src/**"] # after [tasks.codegen] sources = ["src/schema/**"] outputs = ["src/generated/**"]
Defensive patterns
Strategy: validation
Validate before calling
# rough overlap check before enabling cache
python3 - <<'EOF'
import tomllib, sys
cfg = tomllib.load(open('mise.toml','rb'))
for name, t in cfg.get('tasks', {}).items():
if not t.get('cache'): continue
for s in t.get('sources', []):
base = s.rstrip('*').rstrip('/')
for o in t.get('outputs', []):
ob = o.rstrip('*').rstrip('/')
if base and ob and (base.startswith(ob) or ob.startswith(base)):
sys.exit(f"{name}: output '{o}' overlaps source '{s}'")
print('ok')
EOF Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- task {} cache requires at least one source
- task {} cache requires explicit outputs or outputs = []
- task {} cache command input must not be empty: {command:?}
- task {} cache outputs require at least one non-excluded patt
- unsupported task cache store version {}; expected {}
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/8bfe6b5aef3463d2.
Report an issue: GitHub.