Skip to content

Tasks are immediately restarted after unknown "Server indicated the task shouldn't be running anymore", resulting in HTTP 409 #59378

Closed

Description

@rjh-yext

Apache Airflow version

Other Airflow 2/3 version (please specify below)

If "Other Airflow 2/3 version" selected, which one?

apache/airflow:3.1.3 Docker image

What happened?

We're seeing an issue where a task is started, stopped b/c Airflow thinks it should be not be running, then attempts multiple restarts of the task. This results in the task starting execution multiple times, but it appears that Airflow loses track of (or ignores) the execution result. Note that the requests to restart occur within (milli)seconds of the task first starting. In some cases, there are several retries (11x), and the dag is marked as Failed though the offending tasks are marked as Skipped, when they clearly have been attempted multiple times.

Our deployment of Airflow has two instances of the Scheduler running, and we've seen this error occur both when the task is re/started from the same instance, and when it has been re/started from different instances of the Scheduler.

One example of the sequence of Scheduler log entries are as follows. There does not appear to be any other relevant or associated logs within the time frame, but I can provide any further logs if requested. In this case, there are no indications of any error or restart of the tasks in the Dag run logs.

[2025-12-08T08:00:01.522+0000] {{_client.py:1026}} INFO - HTTP Request: PATCH http://myinstance/execution/task-instances/019afcf9-7ee5-713b-be96-758c026e7d15/run "HTTP/1.1 200 OK"

2025-12-08 08:00:01 [debug ] Sending [supervisor] msg=StartupDetails(ti=TaskInstance(id=UUID('019afcf9-7ee5-713b-be96-758c026e7d15'), task_id='MyTask', dag_id='MyDag', run_id='scheduled__2025-12-07T08:00:00+00:00', try_number=1, ...

[2025-12-08T08:01:22.494+0000] {{_client.py:1026}} INFO - HTTP Request: PATCH http://myinstance/execution/task-instances/019afcf9-7ee5-713b-be96-758c026e7d15/run "HTTP/1.1 200 OK"

2025-12-08 08:01:22 [debug ] Sending [supervisor] msg=StartupDetails(ti=TaskInstance(id=UUID('019afcf9-7ee5-713b-be96-758c026e7d15'), task_id='MyTask', dag_id='MyDag', run_id='scheduled__2025-12-07T08:00:00+00:00', try_number=2

[2025-12-08T08:01:22.239+0000] {{_client.py:1026}} INFO - HTTP Request: PUT http://myinstance/execution/task-instances/019afcf9-7ee5-713b-be96-758c026e7d15/heartbeat "HTTP/1.1 409 Conflict"

2025-12-08 08:01:22 [error ] Server indicated the task shouldn't be running anymore [supervisor] detail={'detail': {'reason': 'not_running', 'message': 'TI is no longer in the running state and task should terminate', 'current_state': 'scheduled'}} status_code=409 ti_id=UUID('019afcf9-7ee5-713b-be96-758c026e7d15')

[2025-12-08T08:01:22.642+0000] {{_client.py:1026}} INFO - HTTP Request: PUT http://myinstance/execution/task-instances/019afcf9-7ee5-713b-be96-758c026e7d15/rtif "HTTP/1.1 201 Created"

Occasionally, the dag logs will output something like the following before restarting the task:

2025-12-08 01:18:04.771 | Server indicated the task shouldn't be running anymore. Terminating process

2025-12-08 01:18:04.771 | Task killed!

What you think should happen instead?

No response

How to reproduce

Seems to occur sporadically, and not in any consistent manner. The dags with which this occurs also varies.

Operating System

Debian GNU/Linux 12 (bookworm)

Versions of Apache Airflow Providers

apache-airflow-providers-fab == 3.0.2
apache-airflow-providers-google == 15.1.0
apache-airflow-providers-slack == 9.5.0
apache-airflow-providers-standard == 1.9.0

Deployment

Other Docker-based deployment

Deployment details

There are two instances of Airflow Scheduler deployed

Anything else?

No response

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Activity

  1. boring-cyborg commented on Dec 12, 2025

    @boring-cyborg

    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.

  2. danielford831 commented on Dec 17, 2025

    @danielford831

    Also seeing this on 3.1.4 (which I'll bump to 3.1.5 today, but I notice @rjh-yext is on 3.1.3 so hopefully unrelated)

    2 schedulers on EKS. also useful to note there's no mention of an attempt 1 in the audit log and the UI, but the log for attempt 1 is populated in EFS, attempt 2 log is empty

  3. davidmarshmallow commented on Dec 17, 2025

    @davidmarshmallow

    Also seeing this on 3.1.4

  4. vinitpayal commented on Dec 17, 2025

    @vinitpayal
    Contributor

    Same for us in Airflow 3.1.4.

    In UI randomly seeing task_number starting from 11, 16 etc and because of which task retries we are setting aren't working.

    Screenshot for reference(only one retry with try number 11)

    Image

    Same is the case in metadata database as well

    Image
  5. guillaumecollombet commented on Dec 18, 2025

    @guillaumecollombet

    Same thing in 3.1.5

  6. abhijeets25012-tech commented on Dec 26, 2025

    @abhijeets25012-tech
    Contributor

    Scheduler A Scheduler B
    | |
    RUNNING |
    | |
    Task starts |
    | |
    |--------------> SCHEDULED
    | |
    Heartbeat (409) |
    | |
    Task killed & retried |
    I’m new to open source and this is my first PR, so feedback is very welcome.

    While investigating an issue with multiple schedulers, I observed that a
    TaskInstance can regress from RUNNING back to SCHEDULED due to a race condition.
    This leads to heartbeat conflicts (409), premature task termination, and
    repeated task retries.

    This PR adds a small guard in TaskInstance.set_state() to prevent the invalid
    RUNNING → SCHEDULED transition while allowing all valid state transitions.
    This avoids killing already-running tasks and preserves correct execution.

    Tested locally with a multi-scheduler setup.

  7. dabla commented on Jan 14, 2026

    @dabla
    Contributor

    Scheduler A Scheduler B | | RUNNING | | | Task starts | | | |--------------> SCHEDULED | | Heartbeat (409) | | | Task killed & retried | I’m new to open source and this is my first PR, so feedback is very welcome.

    While investigating an issue with multiple schedulers, I observed that a TaskInstance can regress from RUNNING back to SCHEDULED due to a race condition. This leads to heartbeat conflicts (409), premature task termination, and repeated task retries.

    This PR adds a small guard in TaskInstance.set_state() to prevent the invalid RUNNING → SCHEDULED transition while allowing all valid state transitions. This avoids killing already-running tasks and preserves correct execution.

    Tested locally with a multi-scheduler setup.

    @abhijeets25012-tech I can't find the PR you're mentioning?

  8. dabla commented on Jan 14, 2026

    @dabla
    Contributor

    Issue still there with Airflow 3.1.6, running with 3 schedulers:

    [2026-01-14 06:25:42] INFO - DAG bundles loaded: dags-folder
    [2026-01-14 06:25:42] INFO - Filling up the DagBag from /usr/local/airflow/dags/jb_sccm/dag.py
    [2026-01-14 06:25:42] WARNING - Skipping masking for a secret as it's too short (<5 chars)
    [2026-01-14 06:25:42] INFO - Establishing WinRM connection to host: obfuscated-server-name
    [2026-01-14 06:25:43] INFO - Running command: 'python C:\python_scripts\airflow_bods_trigger\bods_job_trigger.py -j JB_SCCM'...
    [2026-01-14 06:28:29] ERROR - Server indicated the task shouldn't be running anymore. Terminating process detail={"detail":{"reason":"not_found","message":"Task Instance not found"}}
    [2026-01-14 06:28:34] ERROR - Task killed!
    
  9. dabla commented on Jan 14, 2026

    @dabla
    Contributor

    In the meantime found another related issue #5761.

  10. bohdan-pd commented on Jan 26, 2026

    @bohdan-pd

    Same thing in airflow 3.0.6

  11. removed
    needs-triagelabel for new issues that we didn't triage yet
    on Jan 31, 2026
  12. vin-c-ent commented on Feb 11, 2026

    @vin-c-ent
    Contributor

    same here, airflow 3.1.3 with 4 schedulers

  13. 8 remaining items

  14. added 4 commits that reference this issue on Mar 13, 2026
    f7a4f14
    31f4050
    08ece10
    9dd13fb
  15. sam-dumont commented on Mar 13, 2026

    @sam-dumont
    Contributor

    We hit this on our prod cluster (830 DAGs, ~25k tasks/day, 2 schedulers, KubernetesExecutor, ~500 peak concurrent workers on EKS) and spent 2+ weeks instrumenting it. Sharing what we found in case it helps.

    We traced it to at least 2 independent race conditions, both unguarded UPDATEs on task instance state that can overwrite a RUNNING task :

    1. schedule_tis() in dagrun.py : a competing scheduler overwrites RUNNING → SCHEDULED. Worker heartbeats, gets 409 with current_state: scheduled, task killed. PR Fix duplicate task execution when running multiple schedulers (HA) #60330 by @ephraimbuddy addresses this.

    2. ti_skip_downstream() in the Execution API : BranchOperator marks unchosen tasks as SKIPPED, but the UPDATE has no state guard, so it can overwrite tasks already RUNNING. Worker heartbeats, gets 409 with current_state: skipped, task killed. We opened PR Fix ti_skip_downstream overwriting RUNNING tasks to SKIPPED #63266 for this one.

    Both seem to exist in every Airflow 3.x release with 2+ schedulers. The frequency likely scales with concurrency : more schedulers and more tasks = wider race window.

    What worked for us :

    We patched both code paths at Docker build time (patching the installed .py files so every process gets the fix). Our apply_patches.py script does this (auto-detects already-patched files, self-disables when upstream fix lands). Got us from 374 errors/day to 0.

    One thing we learned the hard way : monkey patches in airflow_local_settings.py worked for schedule_tis() (runs in the scheduler) but NOT for ti_skip_downstream(). That one runs on the API server (FastAPI/uvicorn), which has a different startup path and never imports airflow_local_settings.py. We had zero 409s for 18h, then skipped 409s exploded to 131/day when load increased. Build-time patching was the only approach that covered all processes.

    Diagnosing which vector you're hitting :

    Parsing the current_state field from the 409 response body was the key to telling them apart :

    current_state What's happening Relevant PR
    scheduled schedule_tis() race : RUNNING → SCHEDULED PR #60330
    failed Cascade from the above PR #60330
    skipped ti_skip_downstream() race : RUNNING → SKIPPED (branching DAGs) PR #63266
    not_found K8s executor duplicate pod / stale UUID after scheduler restart No fix yet (#57618)

    Our timeline (2+ weeks of continuous monitoring) :

    Date 409s What changed
    Feb 27-Mar 5 14-169 baseline, no fixes
    Mar 6 22 schedule_tis patch deployed : scheduled+failed dropped to 0
    Mar 7-9 3-4 only skipped remaining
    Mar 10 0 both patches active
    Mar 13 0 build-time patches on PROD : 0 race 409s, 0 orphaned tasks

    Note if you're on 3.1.7 specifically : on top of the race conditions above, 3.1.7 has a missing task_reschedule index + DepContext mutation leak (#59604) that slows the scheduler, grows memory (ours peaked at 5.92/6 GiB), and widens the race window for both bugs. This created a feedback loop on our cluster where 409s escalated exponentially (5 → 26 → 133 → 374/day). Upgrading to 3.1.8 broke the loop (#60931, #62089), but the race conditions still need the patches on top.

    Related PRs :

    What PR Status
    schedule_tis() state guard #60330 (@ephraimbuddy) Open, validated on our prod
    ti_skip_downstream() state guard #63266 (ours) Open
    Missing index + DepContext leak (3.1.7 only) #60931, #62089 Fixed in 3.1.8

    (AI-assisted investigation with Claude Code. All monitoring data from our prod cluster.)

  16. dabla commented on Mar 13, 2026

    @dabla
    Contributor

    We hit this on our prod cluster (830 DAGs, ~25k tasks/day, 2 schedulers, KubernetesExecutor, ~500 peak concurrent workers on EKS) and spent 2+ weeks instrumenting it. Sharing what we found in case it helps.

    We traced it to at least 2 independent race conditions, both unguarded UPDATEs on task instance state that can overwrite a RUNNING task :

    1. schedule_tis() in dagrun.py : a competing scheduler overwrites RUNNING → SCHEDULED. Worker heartbeats, gets 409 with current_state: scheduled, task killed. PR Fix HA scheduler try_number double increment #60330 by @ephraimbuddy addresses this.
    2. ti_skip_downstream() in the Execution API : BranchOperator marks unchosen tasks as SKIPPED, but the UPDATE has no state guard, so it can overwrite tasks already RUNNING. Worker heartbeats, gets 409 with current_state: skipped, task killed. We opened PR Fix ti_skip_downstream overwriting RUNNING tasks to SKIPPED #63266 for this one.

    Both seem to exist in every Airflow 3.x release with 2+ schedulers. The frequency likely scales with concurrency : more schedulers and more tasks = wider race window.

    What worked for us :

    We patched both code paths at Docker build time (patching the installed .py files so every process gets the fix). Our apply_patches.py script does this (auto-detects already-patched files, self-disables when upstream fix lands). Got us from 374 errors/day to 0.

    One thing we learned the hard way : monkey patches in airflow_local_settings.py worked for schedule_tis() (runs in the scheduler) but NOT for ti_skip_downstream(). That one runs on the API server (FastAPI/uvicorn), which has a different startup path and never imports airflow_local_settings.py. We had zero 409s for 18h, then skipped 409s exploded to 131/day when load increased. Build-time patching was the only approach that covered all processes.

    Diagnosing which vector you're hitting :

    Parsing the current_state field from the 409 response body was the key to telling them apart :

    current_state What's happening Relevant PR
    scheduled schedule_tis() race : RUNNING → SCHEDULED PR #60330
    failed Cascade from the above PR #60330
    skipped ti_skip_downstream() race : RUNNING → SKIPPED (branching DAGs) PR #63266
    not_found K8s executor duplicate pod / stale UUID after scheduler restart No fix yet (#57618)
    Our timeline (2+ weeks of continuous monitoring) :

    Date 409s What changed
    Feb 27-Mar 5 14-169 baseline, no fixes
    Mar 6 22 schedule_tis patch deployed : scheduled+failed dropped to 0
    Mar 7-9 3-4 only skipped remaining
    Mar 10 0 both patches active
    Mar 13 0 build-time patches on PROD : 0 race 409s, 0 orphaned tasks
    Note if you're on 3.1.7 specifically : on top of the race conditions above, 3.1.7 has a missing task_reschedule index + DepContext mutation leak (#59604) that slows the scheduler, grows memory (ours peaked at 5.92/6 GiB), and widens the race window for both bugs. This created a feedback loop on our cluster where 409s escalated exponentially (5 → 26 → 133 → 374/day). Upgrading to 3.1.8 broke the loop (#60931, #62089), but the race conditions still need the patches on top.

    Related PRs :

    What PR Status
    schedule_tis() state guard #60330 (@ephraimbuddy) Open, validated on our prod
    ti_skip_downstream() state guard #63266 (ours) Open
    Missing index + DepContext leak (3.1.7 only) #60931, #62089 Fixed in 3.1.8
    (AI-assisted investigation with Claude Code. All monitoring data from our prod cluster.)

    Nice investigation and explanation, we don’t have those problems as often anymore. Will try do do the same at our side and see if it also fixes the issue.

    We are running with 3 concurrent schedulers but since we now migrated from YugabyteDB to Postgres again that maybe also helped already. As YugabyteDB isn’t officially supported on Airflow we had to do some post alterations to make it run correctly on YugabyteDB as the Airflow migrations scripts aren’t optimized for it.

    We are running on 3.1.8 so that may also help improve it as you stated in your comment.

  17. bohdan-pd commented on Mar 16, 2026

    @bohdan-pd

    @sam-dumont Thank you! I tested your patch on MWAA and still got this error. Maybe there is another bug as well:
    TI is no longer in the running state and task should terminate

  18. sam-dumont commented on Mar 16, 2026

    @sam-dumont
    Contributor

    @sam-dumont Thank you! I tested your patch on MWAA and still got this error. Maybe there is another bug as well:
    TI is no longer in the running state and task should terminate

    Oh too bad 😔 we still have a few 409 but they are legitimate timeouts (explicit timeouts at the task level). I haven't use MWAA in a while so I'm not sure how easy it is to patch, do you have any control over the docker images?

    If you have control over the airflow_local_settings.py you can use the monkeypatch in this gist : https://gist.github.com/sam-dumont/c362da9c42dd5930266c855c4b8e42fa

    The first one only, the second does not work as it's running on the api server.

    From what I remember MWAA was quite limited in customization and this is why we took the 'custom' road, but we tested 3 years ago so it probably changed since them.

    Hope it'll help... 🤞

  19. added a commit that references this issue on Mar 19, 2026
    6d1794a
  20. added a commit that references this issue on Mar 22, 2026
    f4193a1
  21. added a commit that references this issue on Apr 7, 2026
    6be4738
  22. added a commit that references this issue on Apr 9, 2026
    bd3f270
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    affected_version:3.1Issues Reported for 3.1area:corekind:bugThis is a clearly a bugpriority:highHigh priority bug that should be patched quickly but does not require immediate new release

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions