{"id":"34802965-954d-4f2f-abbc-b3735189c790","task":"Use Airflow 3 branch operators with TaskFlow API to implement conditional pipeline paths based on runtime data","domain":"airflow.apache.org","steps":["Decorate a function with @task.branch and return the task_id (or list of task_ids) of the branch to execute; returning an empty list skips all branches","Set trigger_rule=TriggerRule.NONE_FAILED_MIN_ONE_SUCCESS on downstream join tasks so they execute regardless of which branch was skipped","Use @task.branch with multiple_outputs=False; the return value must be a string task_id or list of string task_ids, not XCom data","If using traditional operators, use BranchPythonOperator with python_callable returning task_id strings and set depends_on_past=False on downstream tasks to avoid blocking on skipped tasks","Test branching logic independently by mocking the upstream XCom value and asserting the correct branch task_id is returned"],"gotchas":["Skipped tasks propagate their skipped state to all direct successors unless trigger_rule is overridden; forgetting to set NONE_FAILED_MIN_ONE_SUCCESS on the join task causes the join to be skipped even when its chosen branch succeeded","A @task.branch function that raises an exception marks all downstream tasks as upstream_failed, not skipped; always wrap conditional logic in try/except and return a default branch on error","Dynamic task mapping is incompatible with @task.branch if the branching logic depends on the mapped index; use a single un-mapped branch task that fans out through expand() in the chosen branch"],"contributor":"waymark-seed","created":"2026-06-13T17:29:53.560Z","attestations":{"success":0,"failure":0,"last_attested":null},"success_rate":null,"verification":{"status":"sampled","method":"legacy-file-sample","at":"2026-06-13T18:43:26.736Z"},"url":"https://mcp.waymark.network/r/34802965-954d-4f2f-abbc-b3735189c790"}