{"record":{"id":"fdc31032418b9a0b","repo":"jdx/mise","slug":"task-cache-command-input-must-not-be-empty-co","errorCode":null,"errorMessage":"task {} cache command input must not be empty: {command:?}","messagePattern":"task (.+?) cache command input must not be empty: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/task/task_cache.rs","lineNumber":1219,"sourceCode":"}\n\npub(crate) fn validate_config(task: &Task, root: &Path) -> Result<()> {\n    if task.sources.is_empty() {\n        bail!(\"task {} cache requires at least one source\", task.name);\n    }\n    if task.outputs.is_auto() {\n        bail!(\n            \"task {} cache requires explicit outputs or outputs = []\",\n            task.name\n        );\n    }\n    if let Some(command) = task.cache.as_ref().and_then(|cache| {\n        cache\n            .command_inputs\n            .iter()\n            .find(|command| command.trim().is_empty())\n    }) {\n        bail!(\n            \"task {} cache command input must not be empty: {command:?}\",\n            task.name\n        );\n    }\n    let patterns = task.outputs.patterns();\n    if !patterns.is_empty() && output_glob_patterns(&patterns).is_empty() {\n        bail!(\n            \"task {} cache outputs require at least one non-excluded pattern\",\n            task.name\n        );\n    }\n    for output in &patterns {\n        let path = if let Some(body) = output.strip_prefix('!') {\n            PathBuf::from(body)\n        } else if let Some(body) = output.strip_prefix(\"\\\\!\") {\n            PathBuf::from(format!(\"!{body}\"))\n        } else {\n            PathBuf::from(&output)","sourceCodeStart":1201,"sourceCodeEnd":1237,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/task/task_cache.rs#L1201-L1237","documentation":"validate_config scans task.cache.command_inputs for entries that are empty after trimming (command.trim().is_empty()). Command inputs are hashed into the cache key, and an empty command would execute nothing while looking like a real input, so the config is rejected.","triggerScenarios":"Declaring command_inputs = [\"\"] or [\"git rev-parse --short HEAD\", \"  \"] — any whitespace-only string in the list triggers the bail with the offending entry shown via {command:?}.","commonSituations":"Templating that renders an optional command to an empty string (e.g. '{{env.GIT_CMD}}' unset); YAML/TOML copy-paste leaving a blank line parsed as an empty item; generated configs that join possibly-empty variables.","solutions":["Remove empty or whitespace-only entries from command_inputs","Guard templated commands so unset variables don't render an empty item","Replace placeholder entries with a real, always-valid command (e.g. 'true' is still questionable — prefer removing)"],"exampleFix":"# before\n[tasks.build]\ncache = true\ncommand_inputs = [\"git rev-parse HEAD\", \"{{env.EXTRA_CMD}}\"]  # EXTRA_CMD unset -> \"\"\n\n# after\n[tasks.build]\ncache = true\ncommand_inputs = [\"git rev-parse HEAD\"]","handlingStrategy":"validation","validationCode":"python3 - <<'EOF'\nimport tomllib, sys\ncfg = tomllib.load(open('mise.toml','rb'))\nfor name, t in cfg.get('tasks', {}).items():\n    cache = t.get('cache', {})\n    if any(not str(c).strip() for c in cache.get('command_inputs', [])):\n        sys.exit(f'{name}: empty command_inputs entry')\nprint('ok')\nEOF","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default templated env vars used in command_inputs to a real command","Don't leave blank placeholders in generated configs"],"tags":["mise","task-cache","caching","configuration","validation"],"backgroundTag":"task-cache-config-invalid","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}