You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
When a parallel task fails allow the other parallel tasks to finish before exiting with failure #1246
Currently when a parallel task fails, Taskfile will kill the other running process.
If you have a running a long-running task like terraform, this will break processes and might leave the task in a bad state.
We should have a config or flag to allow the parallel tasks to complete before quitting with error from the failed task.
This is definitely a must have. I'm currently considering an alternative to Just (a competing tool) because it doesn't support parallelization. And it's great the Task does support it, however the implementation without waiting for the remaining tasks to complete is not acceptable to me.
I'd also like to mention, that the best approach IMO would be to default to waiting for all the tasks to finish and only kill the remaining tasks (fail fast) when explicitly requested by user (e.g. via a CLI flag or a task definition option). At the end of the day Task is not a build tool like Make, but a task runner. Killing tasks mid-execution can have detrimental effects as already mentioned in the original post (e.g. you likely don't want to kill terraform apply), this should not be the default behavior.
This is also how various test frameworks capable of test parallelization usually work, waiting for all the tests to complete before existing and presenting you with the aggregated report unless instructed otherwise. So it's not an entirely new concept. In fact, most of the tools doing some sort of parallelization, in my experience fail fast only when you explicitly ask them to do so.
Reacted by jivoy1988, Kutt Katrea, jenniferabu, Brian Milco, Brennan Kinney, Maximilian Remming, GrumpyDevOps and Elazar Leibovich
I agree with @supersmile2009 that the expected behavior is to finish running the deps/parallel tasks.
Fail-fast could be included as other options (like watch) as both a configuration:
My ugly workaround is to touch a file if there's an error, and then at the end exit nonzero if that file exists. That means I have to clean up that file before starting another run, and it's pretty ugly, but it's a better experience than seeing a stacktrace generated from a program that got sigkilled because another task failed.
Here's a simplified example:
---
version: 3env:
VENV: .venvVENV_ACTIVATE: '{{ .VENV }}/bin/activate'ANS_ENV: .ansible# TODO: Remove this once mvdan's Go sh interpreter auto-sets OSTYPE, which# later versions of Python depend upon being set in the activate script, which# breaks with `set -u` enforced.# https://github.com/mvdan/sh/pull/912OSTYPE:
sh: bash -c 'echo $OSTYPE'TASK_ISSUE_FILE: .task/had-issue# Shell optionsset:
- u # Fail on unset variable referencetasks:
_clean_lint_state:
internal: truesilent: truecmds:
- rm -f {{ .TASK_ISSUE_FILE }} || true_ansible_lint:
internal: truedeps:
- initcmds:
- name: ansible-lint playbookscmd: | ansible-lint --force-color || touch {{ .TASK_ISSUE_FILE }}_yamllint:
internal: truedeps:
- initcmds:
- name: Lint YAML files with yamllintcmd: | yamllint --strict $(git ls-files '*.yaml' '*.yml') || touch {{ .TASK_ISSUE_FILE }}init:
desc: Initialize and install dependenciesrun: oncedeps:
- _clean_lint_statelint:
desc: Run linters to check code for styledeps:
- _yamllint
- _ansible_lintcmds:
- '[[ ! -f {{ .TASK_ISSUE_FILE }} ]]'
My use case is very similar to @andrewimeson's. I have a lint aggregate task which depends on a bunch of individual lint tasks (yamllint, eslint etc).
Consider the following tasks: lint:eslint, lint:yamllint and lint (which depends on the other two). I would like all lint:* tasks to run, and run to completion, even if one of them fails early. The failure should also be reflected in the exit code when running lint.
Without any workarounds:
❌ If one lint:* task fails, the others are killed and their outputs are never displayed.
✔️ The exit code correctly reflects the failure.
The only way that I could find to prevent a failing task from killing others is to use ignore_error: true on the task who's failure shouldn't kill other tasks. With ignore_error: true on all lint:* tasks:
✔️ Failing tasks no longer kill other tasks.
❌ The exit code of the aggregate lint task as well as any individual lint:* task does not reflect the failure.
I don't think ignore_error: true on lint:* tasks is the correct approach, as those should be runnable by themselves and the exit code should reflect any failures. But without ignore_error: true, it's a bloodbath once the first task fails.
There are quite a few comments in the issues where people express the need for the ability to prevent a task from being killed mid-execution. Those comments are typically geared towards long-running tasks, but I think the solution might be the same.
A very easy solution is probably just to mark a task as being shielded from killing. On failure, shielded tasks are allowed to complete and then the exit code reflects whatever the first failure was.
Not killing tasks is one thing, but another thing is to ensure that all tasks have had the opportunity to run. This is more relevant to the case of linters. In other cases, you might not care that subsequent tasks have never run if prior tasks have failed, as long as you don't kill something mid-execution. When linting, you want everything to run, and run to completion.
I just opened a PR to implement a setting for this.
We're basically changing the default behavior to always wait, and adding failfast: true or --failfast to opt for the previous behavior (stop once any fail).
Please give that branch a try if you can and let me know if it works well for you.
Activity
supersmile2009 commentedon Sep 19, 2023
This is definitely a must have. I'm currently considering an alternative to Just (a competing tool) because it doesn't support parallelization. And it's great the Task does support it, however the implementation without waiting for the remaining tasks to complete is not acceptable to me.
I'd also like to mention, that the best approach IMO would be to default to waiting for all the tasks to finish and only kill the remaining tasks (fail fast) when explicitly requested by user (e.g. via a CLI flag or a task definition option). At the end of the day Task is not a build tool like Make, but a task runner. Killing tasks mid-execution can have detrimental effects as already mentioned in the original post (e.g. you likely don't want to kill
terraform apply), this should not be the default behavior.This is also how various test frameworks capable of test parallelization usually work, waiting for all the tests to complete before existing and presenting you with the aggregated report unless instructed otherwise. So it's not an entirely new concept. In fact, most of the tools doing some sort of parallelization, in my experience fail fast only when you explicitly ask them to do so.
KuttKatrea commentedon Jan 6, 2024
I agree with @supersmile2009 that the expected behavior is to finish running the deps/parallel tasks.
Fail-fast could be included as other options (like watch) as both a configuration:
and a command line option:
andrewimeson commentedon Jan 16, 2024
My ugly workaround is to touch a file if there's an error, and then at the end exit nonzero if that file exists. That means I have to clean up that file before starting another run, and it's pretty ugly, but it's a better experience than seeing a stacktrace generated from a program that got sigkilled because another task failed.
Here's a simplified example:
bennieswart commentedon Nov 12, 2025
My use case is very similar to @andrewimeson's. I have a
lintaggregate task which depends on a bunch of individual lint tasks (yamllint, eslint etc).Consider the following tasks:
lint:eslint,lint:yamllintandlint(which depends on the other two). I would like alllint:*tasks to run, and run to completion, even if one of them fails early. The failure should also be reflected in the exit code when runninglint.Without any workarounds:
❌ If one
lint:*task fails, the others are killed and their outputs are never displayed.✔️ The exit code correctly reflects the failure.
The only way that I could find to prevent a failing task from killing others is to use
ignore_error: trueon the task who's failure shouldn't kill other tasks. Withignore_error: trueon alllint:*tasks:✔️ Failing tasks no longer kill other tasks.
❌ The exit code of the aggregate
linttask as well as any individuallint:*task does not reflect the failure.I don't think
ignore_error: trueonlint:*tasks is the correct approach, as those should be runnable by themselves and the exit code should reflect any failures. But withoutignore_error: true, it's a bloodbath once the first task fails.There are quite a few comments in the issues where people express the need for the ability to prevent a task from being killed mid-execution. Those comments are typically geared towards long-running tasks, but I think the solution might be the same.
A very easy solution is probably just to mark a task as being shielded from killing. On failure, shielded tasks are allowed to complete and then the exit code reflects whatever the first failure was.
Not killing tasks is one thing, but another thing is to ensure that all tasks have had the opportunity to run. This is more relevant to the case of linters. In other cases, you might not care that subsequent tasks have never run if prior tasks have failed, as long as you don't kill something mid-execution. When linting, you want everything to run, and run to completion.
andreynering commentedon Nov 22, 2025
Hey everybody!
I just opened a PR to implement a setting for this.
We're basically changing the default behavior to always wait, and adding
failfast: trueor--failfastto opt for the previous behavior (stop once any fail).Please give that branch a try if you can and let me know if it works well for you.
--failfastandfailfast: trueto control dependencies #2525elvism-lullabot commentedon Nov 24, 2025
great addition, thank you!