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
Airflow task is marked as succeeded on the EcsTaskFailToStart exception #52943
When using EcsRunTaskOperator, if an ECS task fails to start (e.g., due to resource constraints or misconfiguration), the _check_success_task method raises EcsTaskFailToStart. The @AwsBaseHook.retry(should_retry_eni) decorator retries this exception. However, the arn is set to None, so the function exits as success.
@AwsBaseHook.retry(should_retry_eni)
def _check_success_task(self) -> None:
if not self.client or not self.arn:
return
response = self.client.describe_tasks(cluster=self.cluster, tasks=[self.arn])
self.log.info("ECS Task stopped, check status: %s", response)
if len(response.get("failures", [])) > 0:
raise AirflowException(response)
for task in response["tasks"]:
if task.get("stopCode", "") == "TaskFailedToStart":
# Reset task arn here otherwise the retry run will not start
# a new task but keep polling the old dead one
# I'm not resetting it for other exceptions here because
# EcsTaskFailToStart is the only exception that's being retried at the moment
self.arn = None
raise EcsTaskFailToStart(f"The task failed to start due to: {task.get('stoppedReason', '')}")
Apache Airflow version
2.7.2
Operating System
Ubuntu 22.04
Deployment
Other
Deployment details
No response
What happened
The Airflow task is marked as successful even though the ECS task never started successfully.
What you think should happen instead
The Airflow task should be marked as failed if the ECS task fails to start and all retries are exhausted.
Thanks for opening your first issue here! Be sure to follow the issue template! If you are willing to raise PR to address this issue please do so, no need to wait for approval.
@fweilun The way to reproduce the issue is to create an ECS Task Definition with a wrong command that the container cannot execute (run-script in my example).
In that case, the describe_tasks will return the following JSON.
{
"tasks": [
{
...
"stopCode": "TaskFailedToStart",
"stoppedReason": "CannotStartContainerError: ResourceInitializationError: failed to create new container runtime task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: run-script: executable file not found in $PATH: unknown",
...
}
],
"failures": [],
"ResponseMetadata": {
...
}
}
There is, for some reason, a special handling of TaskFailedToStartResourceInitializationError, which leads to a retry of _check_success_task function (that without arn returns None):
defshould_retry_eni(exception: Exception):
"""Check if exception is related to ENI (Elastic Network Interfaces)."""ifisinstance(exception, EcsTaskFailToStart):
returnany(
eni_reasoninexception.messageforeni_reasonin ["network interface provisioning", "ResourceInitializationError"]
)
returnFalse
I also reproduced the behavior when the task is TaskFailedToStart, but the stoppedReason is 'CannotPullContainerError: pull image manifest has been retried 1 time(s), then Airflow marks the DAG as failed, as expected.
I don't understand the reason for this custom handling of TaskFailedToStart errors that lead to retries in specific cases.
I used this operator with Airflow 3.1.0 to reproduce the error by adding "command": ["run-script"] to the container definition, which triggered a ResourceInitializationError.
The task is correctly marked as failed, but I’m not sure if the retry logic was triggered. Is there any difference from our logs?
Starting call to '***.providers.amazon.aws.operators.ecs.EcsRunTask0perator._check_success_task', this is the lst time calling it.
ECS Task stopped, check status: ('tasks': [{'attachments': [...
Finished call to '***.providers.amazon.aws.operators.ecs.EcsRunTask0perator._check_success_task' after 0.395(s), this was the ist time cal
Starting call to '***.providers.amazon.aws.operators.ecs.EcsRunTask0perator._check_success_task', this is the 2nd time calling it.
Marking task as SUCCESS. dag_id=.... task_id=main_task, execution_date=20250706T080803, start_date=2025076
Starting/Finished call logs come from the tenacity_before_logger and tenacity_after_logger functions that are defined here
The original author resets self.arn to None when a TaskFailedToStart error occurs, so that a new ECS task can be started instead of repeatedly checking the same failed one.
However, reset of self.arn isn't reset on retry. Instead, the method returns early as successful if self.client or self.arn is None, which leads to this issue.
I think self.client or self.arn being None should raise an error instead of silently returning.
I added the following block to address the issue — could you check if this solution is appropriate? I’m unsure whether calling _start_task here would cause any issues with the framework.
@AwsBaseHook.retry(should_retry_eni)def_check_success_task(self) ->None:
ifnotself.arn:
self._start_task()
self._wait_for_task_ended()
ifnotself.clientornotself.arn:
returnresponse=self.client.describe_tasks(cluster=self.cluster, tasks=[self.arn])
self.log.info("ECS Task stopped, check status: %s", response)
iflen(response.get("failures", [])) >0:
raiseAirflowException(response)
fortaskinresponse["tasks"]:
iftask.get("stopCode", "") =="TaskFailedToStart":
# Reset task arn here otherwise the retry run will not start# a new task but keep polling the old dead one# I'm not resetting it for other exceptions here because# EcsTaskFailToStart is the only exception that's being retried at the momentself.arn=NoneraiseEcsTaskFailToStart(f"The task failed to start due to: {task.get('stoppedReason', '')}")
# This is a `stoppedReason` that indicates a task has not# successfully finished, but there is no other indication of failure# in the response.# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/stopped-task-errors.htmlifre.match(r"Host EC2 \(instance .+?\) (stopped|terminated)\.", task.get("stoppedReason", "")):
raiseAirflowException(
f"The task was stopped because the host instance terminated:"f" {task.get('stoppedReason', '')}"
)
containers=task["containers"]
forcontainerincontainers:
ifcontainer.get("lastStatus") =="STOPPED"andcontainer.get("exitCode", 1) !=0:
ifself.task_log_fetcher:
last_logs="\n".join(
self.task_log_fetcher.get_last_log_messages(self.number_logs_exception)
)
raiseAirflowException(
f"This task is not in success state - last {self.number_logs_exception} "f"logs from Cloudwatch:\n{last_logs}"
)
raiseAirflowException(f"This task is not in success state {task}")
ifcontainer.get("lastStatus") =="PENDING":
raiseAirflowException(f"This task is still pending {task}")
if"error"incontainer.get("reason", "").lower():
raiseAirflowException(
f"This containers encounter an error during launching: "f"{container.get('reason', '').lower()}"
)
@fweilun
According to AWS documentation (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/resource-initialization-error.html), most ResourceInitializationErrors are caused by misconfiguration in the Task Definition or Permissions; so, retry would not help in that case. Probably, there are some edge cases where retry should be implemented. Are there any contact points in AWS that can help define these cases?
I'm using the ESC operator with the following flags: deferrable=True and wait_for_completion=False. Therefore, the function that should be used in the retry is self._execute, not self._start_task. The AwsBaseHook.retry mechanism currently doesn't support this mode, as the current retry attempt is not passed to the next task execution (which would lead to an infinite retry) and can only be performed in-process.
Only ECS Operator use this retry logic; all other operators will fail in any case of AWS internal error. I suggest completely turning off the retry mechanism.
@vladimirbinshtok Thanks for the review — I see the problem now. I believe we should keep the should_retry_eni function, since “network interface provisioning” errors are still related to ENI issues, as confirmed here.
@fweilun The code currently doesn't attempt to retry the task; it only retries the _check_success_task function call (which is pointless). The implemented retry mechanism doesn't support deferrable and wait_for_completion flags, which would be pretty complicated to add, as you would need to pass the current retry number to the new task outside of the standard Airflow retry policy. No other Amazon operator has a retry policy for task failed to start errors (any service can have initialization issues), so I suggest aligning the same approach with the ECS operator.
As mentioned in the document you attached, the solution to solve the Fargate start task error is to use Step Function (Step Function operator in Airflow), which will allow an independent retry mechanism.
Activity
boring-cyborg commentedon Jul 6, 2025
Thanks for opening your first issue here! Be sure to follow the issue template! If you are willing to raise PR to address this issue please do so, no need to wait for approval.
fweilun commentedon Jul 7, 2025
I'd like to address this issue. @vladimirbinshtok Could you give me an example on how to reproduce this?
vladimirbinshtok commentedon Jul 7, 2025
@fweilun The way to reproduce the issue is to create an ECS Task Definition with a wrong command that the container cannot execute (run-script in my example).
In that case, the
describe_taskswill return the following JSON.There is, for some reason, a special handling of
TaskFailedToStartResourceInitializationError, which leads to a retry of_check_success_taskfunction (that withoutarnreturns None):I also reproduced the behavior when the task is
TaskFailedToStart, but the stoppedReason is'CannotPullContainerError: pull image manifest has been retried 1 time(s), then Airflow marks the DAG as failed, as expected.I don't understand the reason for this custom handling of
TaskFailedToStarterrors that lead to retries in specific cases.fweilun commentedon Jul 8, 2025
I used this operator with Airflow 3.1.0 to reproduce the error by adding "command": ["run-script"] to the container definition, which triggered a ResourceInitializationError.
The task is correctly marked as failed, but I’m not sure if the retry logic was triggered. Is there any difference from our logs?
vladimirbinshtok commentedon Jul 8, 2025
@fweilun I'm getting the following log messages:
Starting/Finished call logs come from the
tenacity_before_loggerandtenacity_after_loggerfunctions that are defined hereAlso, I missed in the description that I'm using ECS operator in deferable mode.
fweilun commentedon Jul 8, 2025
The original author resets self.arn to None when a
TaskFailedToStarterror occurs, so that a new ECS task can be started instead of repeatedly checking the same failed one.However, reset of
self.arnisn't reset on retry. Instead, the method returns early as successful ifself.clientorself.arnis None, which leads to this issue.I think
self.clientorself.arnbeing None should raise an error instead of silently returning.I added the following block to address the issue — could you check if this solution is appropriate? I’m unsure whether calling
_start_taskhere would cause any issues with the framework.Revised
_check_success_task:vladimirbinshtok commentedon Jul 9, 2025
@fweilun
According to AWS documentation (https://docs.aws.amazon.com/AmazonECS/latest/developerguide/resource-initialization-error.html), most ResourceInitializationErrors are caused by misconfiguration in the Task Definition or Permissions; so, retry would not help in that case. Probably, there are some edge cases where retry should be implemented. Are there any contact points in AWS that can help define these cases?
I'm using the ESC operator with the following flags:
deferrable=Trueandwait_for_completion=False. Therefore, the function that should be used in the retry isself._execute, notself._start_task. The AwsBaseHook.retry mechanism currently doesn't support this mode, as the current retry attempt is not passed to the next task execution (which would lead to an infinite retry) and can only be performed in-process.Only ECS Operator use this retry logic; all other operators will fail in any case of AWS internal error. I suggest completely turning off the retry mechanism.
vladimirbinshtok commentedon Jul 9, 2025
@fweilun Since
CannotPullContainerErroris not part ofResourceInitializationError, there is no retry on it, so you don't need to disable it.https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_cannot_pull_image.html
fweilun commentedon Jul 10, 2025
@vladimirbinshtok Thanks for the review — I see the problem now. I believe we should keep the should_retry_eni function, since “network interface provisioning” errors are still related to ENI issues, as confirmed here.
vladimirbinshtok commentedon Jul 10, 2025
@fweilun The code currently doesn't attempt to retry the task; it only retries the
_check_success_taskfunction call (which is pointless). The implemented retry mechanism doesn't supportdeferrableandwait_for_completionflags, which would be pretty complicated to add, as you would need to pass the current retry number to the new task outside of the standard Airflow retry policy. No other Amazon operator has a retry policy for task failed to start errors (any service can have initialization issues), so I suggest aligning the same approach with the ECS operator.As mentioned in the document you attached, the solution to solve the Fargate start task error is to use Step Function (Step Function operator in Airflow), which will allow an independent retry mechanism.