jdx/mise · error
path must identify an output beneath the task directory
Error message
path must identify an output beneath the task directory
What it means
The final ensure_safe_relative check: a valid cache path must contain at least one Component::Normal. Paths like "." or strings that normalize to no real component do not identify an output beneath the task directory, so they are rejected even though they are relative and non-escaping.
Source
Thrown at src/task/task_cache.rs:1265
build_output_matcher(root, &patterns)
.wrap_err_with(|| format!("task {} has an invalid cache output pattern", task.name))?;
Ok(())
}
fn ensure_safe_relative(path: &Path) -> Result<()> {
if path.as_os_str().is_empty() || path.is_absolute() {
bail!("path must be a non-empty relative path");
}
if path.components().any(|c| {
matches!(
c,
Component::ParentDir | Component::RootDir | Component::Prefix(_)
)
}) {
bail!("path must not escape the task directory");
}
if !path.components().any(|c| matches!(c, Component::Normal(_))) {
bail!("path must identify an output beneath the task directory");
}
Ok(())
}
fn resolve_output_roots(task: &Task, root: &Path, require_matches: bool) -> Result<Vec<PathBuf>> {
let mut resolved = BTreeSet::new();
let patterns = task.outputs.patterns();
let matcher = build_output_matcher(root, &patterns)?;
for output in output_glob_patterns(&patterns) {
ensure_safe_relative(Path::new(&output))?;
if crate::task::task_source_checker::is_glob_pattern(&output) {
let mut glob_matched = false;
for expanded in expand_enumeration_patterns(&output)? {
ensure_safe_relative(Path::new(&expanded))?;
for entry in glob(root.join(expanded).to_str().unwrap_or_default())? {
let path = entry?;
glob_matched = true;
let rel = path.strip_prefix(root)?.to_path_buf();View on GitHub (pinned to 6f52dcdf99)
Solutions
- Name concrete output paths or globs instead of '.'
- Use outputs = [] plus explicit inclusion globs where possible
- If seen during restore, delete the cache entry — manifests written by mise never contain such roots
Example fix
# before outputs = ["."] # after outputs = ["dist/**", "build/**"]
Defensive patterns
Strategy: validation
Validate before calling
python3 - <<'EOF'
import tomllib, sys, os
cfg = tomllib.load(open('mise.toml','rb'))
for name, t in cfg.get('tasks', {}).items():
for o in map(str, t.get('outputs', [])):
p = o.lstrip('\\').lstrip('!')
if os.path.normpath(p) in ('.', ''):
sys.exit(f'{name}: output must name a real path, not {o!r}')
print('ok')
EOF Prevention
- Use concrete paths/globs; '.' is not a valid cache root
- Enumerate actual output dirs instead of caching the cwd
When it happens
Trigger: outputs = ["."] (cache the whole cwd as a single 'root' of '.'), or an output pattern that reduces to a bare current-directory component; also fires for malformed manifest roots read at restore time.
Common situations: Users trying to cache 'everything the task touched' with outputs = ["."]; templating that collapses to '.'; edge-case manifests produced by other tools.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- path must be a non-empty relative path
- 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
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/2246c3dd229b3c4f.
Report an issue: GitHub.