Skip to content

Cyclic TaskGroup dependencies break Grid/Graph since 3.3.1; proposed parse-time rejection is a breaking change #73678

Description

@dheerajturaga

Starting with Airflow 3.3.1, the Grid and Graph views return HTTP 500 for Dags whose TaskGroups have dependencies that form a cycle when each group is treated as a single unit, even though the task-level graph is acyclic. These Dags parse, schedule and run normally, and they rendered in the UI up to and including 3.3.0.

One proposed change (#73087) would reject these Dags at parse time. That is also a breaking change for existing Dags, this time with an import error. Before choosing a direction, I'd like community input on:

  • how to handle the regression,
  • whether these Dags should be allowed at all,
  • how any change should be rolled out and communicated.

I'm affected by this on my own Dags as well.

How we got here

Affected Dag shapes

A task with no upstream task inside its own group counts as a root of that group, even when a task outside the group provides its only upstream. Group-level edges are built from those roots.

1. Sibling groups with dependencies in both directions:

from airflow.providers.standard.operators.empty import EmptyOperator
from airflow.sdk import DAG, TaskGroup

with DAG("tg_cycle", schedule=None):
    with TaskGroup("group1"):
        a1 = EmptyOperator(task_id="a1")
        a2 = EmptyOperator(task_id="a2")
    with TaskGroup("group2"):
        b1 = EmptyOperator(task_id="b1")
        b2 = EmptyOperator(task_id="b2")

    a1 >> b1  # group1 -> group2
    b2 >> a2  # group2 -> group1

2. Two tasks in one group bridged by a task outside it:

with DAG("tg_bridged", schedule=None):
    with TaskGroup("g"):
        a = EmptyOperator(task_id="a")
        b = EmptyOperator(task_id="b")
    ext = EmptyOperator(task_id="ext")

    a >> ext >> b  # g -> ext -> g

Both are acyclic at the task level. This pattern is common when TaskGroups are used for logical or visual grouping (for example one group per database schema) rather than as execution units. Users report hitting it when upgrading from 2.x to 3.3.1.

Impact by version

Version Parsing / scheduling Grid / Graph view
≤ 3.3.0 Works Renders, but group order can be wrong
3.3.1, 3.3.2 Works HTTP 500
With #73087 Import error, Dag not loaded N/A

Open questions for the community

  1. Should these Dags be allowed at all?
  2. How should the change be rolled out, given the deprecation policy? Options include:
  3. What should 3.3.x users do in the meantime? Currently the only workaround is to restructure the Dag: remove the cycle or flatten the affected TaskGroups. Tasks keep running and stay reachable through the REST API.
  4. Communication: a significant newsfragment exists in Reject cyclic TaskGroup dependencies during Dag parsing #73087. Should there also be a known-issue note in the 3.3.1/3.3.2 release notes and/or an announcement on the dev/users mailing list?

Related

Are you willing to submit PR?

  • Yes. I'm willing to submit PRs for whichever direction the community agrees on.

Drafted-by: Claude Code (Opus 5.5); reviewed by @dheerajturaga before posting

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions