{"record":{"id":"02844ce5546ebf16","repo":"nikivdev/code","slug":"no-lifecycle-down-action-found-define-task-down","errorCode":null,"errorMessage":"No lifecycle down action found. Define task 'down', set [lifecycle].down_task, or enable [lifecycle.domains] cleanup in {}","messagePattern":"No lifecycle down action found\\. Define task 'down', set \\[lifecycle\\]\\.down_task, or enable \\[lifecycle\\.domains\\] cleanup in (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/lifecycle.rs","lineNumber":79,"sourceCode":"    if !task_ran && lifecycle.down_task.is_none() {\n        processes::kill_processes(KillOpts {\n            config: project.flow_path.clone(),\n            task: None,\n            pid: None,\n            all: true,\n            force: false,\n            timeout: 5,\n        })?;\n        task_ran = true;\n    }\n\n    let mut domain_action_ran = false;\n    if let Some(domains_cfg) = lifecycle.domains.as_ref() {\n        domain_action_ran = run_domains_down(domains_cfg)?;\n    }\n\n    if !task_ran && !domain_action_ran {\n        bail!(\n            \"No lifecycle down action found. Define task 'down', set [lifecycle].down_task, or enable [lifecycle.domains] cleanup in {}\",\n            project.flow_path.display()\n        );\n    }\n\n    Ok(())\n}\n\nfn run_required_task(config_path: &Path, task_name: &str, args: Vec<String>) -> Result<bool> {\n    match run_task(config_path, task_name, args) {\n        Ok(()) => Ok(true),\n        Err(err) if is_task_not_found(&err) => {\n            bail!(\"lifecycle task '{}' not found\", task_name);\n        }\n        Err(err) => Err(err),\n    }\n}\n","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/lifecycle.rs#L61-L97","documentation":"run_down checks that at least one teardown action exists and ran: a 'down' task, an explicit [lifecycle].down_task, or a [lifecycle.domains] cleanup. If neither the task chain nor any domain action ran, it bails pointing at the flow.toml path. Like run_up, it's a config-completeness guard.","triggerScenarios":"Running the down lifecycle command when flow.toml has no 'down' task, no [lifecycle].down_task, and either no [lifecycle.domains] section or run_domains_down performed no action.","commonSituations":"Project defines up tasks but never wired teardown; down task renamed without updating down_task; [lifecycle.domains] configured but its engine produced no runnable cleanup; editing the wrong flow.toml.","solutions":["Add a `down` task to flow.toml under [tasks]","Or set [lifecycle] down_task = \"teardown\" pointing at an existing task","Or configure [lifecycle.domains] so a domain cleanup action can run"],"exampleFix":"// before (flow.toml)\n[tasks]\nup = \"docker compose up -d\"\n// after\n[tasks]\nup = \"docker compose up -d\"\ndown = \"docker compose down\"\n[lifecycle]\ndown_task = \"down\"","handlingStrategy":"validation","validationCode":"let cfg = std::fs::read_to_string(\"flow.toml\")?;\nlet has_down = cfg.contains(\"down_task\")\n    || cfg.lines().any(|l| l.starts_with(\"down \"))\n    || cfg.contains(\"[lifecycle.domains]\");\nif !has_down { eprintln!(\"flow.toml defines no down action\"); }","typeGuard":null,"tryCatchPattern":"if let Err(e) = run_down(&project, opts) {\n    if e.to_string().contains(\"No lifecycle down action found\") {\n        eprintln!(\"Add a 'down' task, [lifecycle].down_task, or [lifecycle.domains] cleanup\");\n    }\n    return Err(e);\n}","preventionTips":["Pair every up task with a down task in project templates","Configure [lifecycle].down_task explicitly rather than relying on conventions","Verify teardown works with a smoke test after adding lifecycle config"],"tags":["config","lifecycle","missing-task"],"backgroundTag":"missing-config-task","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}