{"record":{"id":"7a6d41a451c2b895","repo":"jdx/mise","slug":"path-must-be-a-non-empty-relative-path","errorCode":null,"errorMessage":"path must be a non-empty relative path","messagePattern":"path must be a non-empty relative path","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/task_cache.rs","lineNumber":1254,"sourceCode":"        } else {\n            PathBuf::from(&output)\n        };\n        ensure_safe_relative(&path).wrap_err_with(|| {\n            format!(\n                \"task {} cache output must stay within {}: {output}\",\n                task.name,\n                root.display()\n            )\n        })?;\n    }\n    build_output_matcher(root, &patterns)\n        .wrap_err_with(|| format!(\"task {} has an invalid cache output pattern\", task.name))?;\n    Ok(())\n}\n\nfn ensure_safe_relative(path: &Path) -> Result<()> {\n    if path.as_os_str().is_empty() || path.is_absolute() {\n        bail!(\"path must be a non-empty relative path\");\n    }\n    if path.components().any(|c| {\n        matches!(\n            c,\n            Component::ParentDir | Component::RootDir | Component::Prefix(_)\n        )\n    }) {\n        bail!(\"path must not escape the task directory\");\n    }\n    if !path.components().any(|c| matches!(c, Component::Normal(_))) {\n        bail!(\"path must identify an output beneath the task directory\");\n    }\n    Ok(())\n}\n\nfn resolve_output_roots(task: &Task, root: &Path, require_matches: bool) -> Result<Vec<PathBuf>> {\n    let mut resolved = BTreeSet::new();\n    let patterns = task.outputs.patterns();","sourceCodeStart":1236,"sourceCodeEnd":1272,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/task/task_cache.rs#L1236-L1272","documentation":"ensure_safe_relative rejects output/manifest paths whose os-string is empty or that are absolute (path.is_absolute()). Cache roots must be relative so they can be joined onto the task root and staged safely; an absolute or empty path breaks that invariant.","triggerScenarios":"outputs containing \"/dist\", \"C:\\\\out\", an absolute path that survived templating, or an empty string (e.g. from '{{env.OUT_DIR}}' with the variable unset); the check also runs on manifest roots during restore via ensure_safe_relative(root).","commonSituations":"Templated output paths with missing environment variables rendering to \"\"; Windows-style absolute paths; users trying to cache files outside the project by specifying an absolute path.","solutions":["Make the output a relative path under the task/project root: 'dist' not '/dist'","Give templated variables a sane relative default or fail the template when unset","For files outside the project, copy or symlink them into the project before caching"],"exampleFix":"# before\noutputs = [\"{{env.OUT_DIR}}/app.bin\"]  # OUT_DIR unset/absolute\n\n# after\noutputs = [\"dist/app.bin\"]","handlingStrategy":"validation","validationCode":"python3 - <<'EOF'\nimport tomllib, sys, os\ncfg = tomllib.load(open('mise.toml','rb'))\nfor name, t in cfg.get('tasks', {}).items():\n    for o in map(str, t.get('outputs', [])):\n        p = o.lstrip('\\\\').lstrip('!')\n        if not p or os.path.isabs(p):\n            sys.exit(f'{name}: output must be non-empty and relative: {o!r}')\nprint('ok')\nEOF","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep output paths relative to the project root","Validate templated env vars used in outputs before enabling cache"],"tags":["mise","task-cache","caching","paths","validation"],"backgroundTag":"invalid-relative-path","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}