sjyangkevin opened a new pull request, #74528:
URL: https://github.com/apache/airflow/pull/74528

   ### Why
   
   closes: #61850
   
   The "External DAG" link on `ExternalTaskSensor` reused the sensor's own run 
ID under the external Dag, so it usually opened a run that does not exist. That 
happens whenever `execution_delta` or `execution_date_fn` targets a different 
logical date (the case in the issue). It also happens with the same logical 
date when the two runs differ in run type or `run_after`, for example a 
backfilled or manually triggered downstream run waiting on a scheduled upstream 
run.
   
   On Airflow 3 the link is built on the worker after the task finishes and 
stored as XCom, so it cannot query the metadata database. Instead, when the 
sensor reaches an outcome (success, a `failed_states` / `skipped_states` hit, 
or a deferred sensor resuming), it looks up the runs it matched through the 
Execution API, using the same logical dates it polled for. It uses 
`get_task_states`, which is keyed by run ID and available since Airflow 3.0, 
and pushes the run IDs to a new `external_run_ids` XCom. The link then opens:
   
   - the matched run, when there is exactly one;
   - the external Dag's runs list, when `execution_date_fn` matched several 
runs;
   - the external Dag's page, when no run has been matched yet (sensor still 
waiting, or timed out), instead of a guessed run URL.
   
   The Airflow 2 code path is unchanged.
   
   #### Notes
   
   - Existing tests changed because the behaviour changed on purpose:
     - `test_link_points_to_external_dag_run` and 
`test_external_task_sensor_extra_link` asserted the old guessed URL; they now 
expect the Dag-page fallback.
     - The two task-group tests now also expect the second `get_task_states` 
call that records the run IDs.
     - Four deferrable tests now pass a `ti` in the context, as a real resume 
does.
   - An external run without task instances can't be found this way, so the 
link falls back to the Dag page. Successful runs always have task instances.
   - `ExternalTaskMarker` shares the link. As an `EmptyOperator` it rarely runs 
on a worker, but when it does it now links to the external Dag instead of a 
guessed run.
   
   #### Testing
   
   - `pytest 
providers/standard/tests/unit/standard/sensors/test_external_task.py`: 51 
passed, 65 skipped (Airflow 2-only). The new tests run the sensor through the 
real task runner and read the link the way the API does; the 15 new or changed 
tests fail against the previous implementation.
   - End to end with `airflow dags test`, mirroring the issue (upstream on `30 
22 * * *`, downstream on `35 22 * * *` waiting via `execution_date_fn` or 
`execution_delta`). Before the fix, the stored link was 
`/dags/<upstream>/runs/<downstream's own run ID>`, a run that doesn't exist. 
After the fix, it opens the upstream run the sensor matched.
   - Same setup with an upstream run that fails and `failed_states=["failed"]` 
on the sensor: the sensor fails and the link opens the failed upstream run.
   - Manually in the UI with `breeze start-airflow`, the External DAG link 
resolved to the specific run in upstream DAG
   
   <img width="1120" height="959" alt="Screenshot from 2026-10-09 22-48-09" 
src="https://github.com/user-attachments/assets/5d0237cd-80d5-47f9-9ff3-3bb4e8410f2c";
 />
   
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   - [X] Yes — Claude Code (Opus 5.5)
   
   Generated-by: Claude Code (Opus 5.5) following [the 
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to