ccoliu opened a new issue, #76838:
URL: https://github.com/apache/airflow/issues/76838
### Under which category would you file this issue?
Airflow Core
### Apache Airflow version
3.4.0
### What happened and how to reproduce it?
When a task inside a task loop has an asset outlet, each iteration emits its
own asset event. On the asset's page, every one of those events has the same
"Source" link, `/dags/<dag_id>/runs/<run_id>/tasks/<task_id>`, with no loop
coordinate. Clicking it opens the task instance page with "No Task Instance
found" and a `400 Bad Request: Select a region and index for this task
instance` error, because the task instance API rejects a looped task instance
requested without its region:
```
GET
/api/v2/dags/combo_asset_producer/dagRuns/combo1/taskInstances/body.produce
-> 400
GET
/api/v2/dags/combo_asset_producer/dagRuns/combo1/taskInstances/body.produce/-1
-> 400
{"detail": "Select a region and index for this task instance"}
```
So there is no way to get from an asset event to the iteration that produced
it, and the three links can't be told apart.
<img width="1500" height="900" alt="Image"
src="https://github.com/user-attachments/assets/10aef87e-90aa-4ef7-8e59-6b19613a2385"
/>
The link is built by hand in
`airflow-core/src/airflow/ui/src/components/Assets/AssetEvent.tsx`:
```tsx
to={`/dags/${event.source_dag_id}/runs/${event.source_run_id}/tasks/${event.source_task_id}${event.source_map_index
> -1 ? `/mapped/${event.source_map_index}` : ""}`}
```
Other task instance links in the UI go through `getTaskInstanceLink()`,
which adds `region_id` and `region_index` for loop task instances.
`AssetEventResponse` doesn't carry the source task instance's region, though
(only `source_task_instance_id`, which is set and distinct per iteration), so
the UI can't build the right link from the event alone.
#### How to reproduce
```python
import pendulum
from airflow.sdk import DAG, Asset, task, task_group
LOOP_ASSET = Asset("loop_combo_asset")
with DAG("combo_asset_producer", start_date=pendulum.datetime(2026, 1, 1,
tz="UTC"), schedule=None):
@task_group
def body():
@task(outlets=[LOOP_ASSET])
def produce(*, loop, outlet_events):
outlet_events[LOOP_ASSET].extra = {"iteration": loop.index}
return loop.index
produce()
def stop_at_three(*, loop):
return loop.result >= 3
body.loop(max_iterations=5, until=stop_at_three)
```
1. Trigger `combo_asset_producer`. It runs 3 iterations and emits 3 asset
events (`extra` = `{"iteration": 1}`, `2`, `3`).
2. Open **Assets → loop_combo_asset**. All three events show the same
"Source" link.
3. Click any of them: the task instance page shows "No Task Instance found"
and the 400 error above.
The task instance's own **Asset Events** tab lists the right events, since
it filters by `source_task_instance_id` for loop task instances, but the
"Source" link on each of them has the same problem.
### What you think should happen instead?
Each event's "Source" link should open the task instance of the iteration
that emitted it.
One option is to add the source task instance's `region_id` and
`region_index` to `AssetEventResponse` (the event already references the task
instance through `source_task_instance_id`), and build the link with
`getTaskInstanceLink()`. I have a fix along these lines and will open a PR
right after this issue.
### Operating System
Docker Compose (official `apache/airflow:3.4.0` image, CeleryExecutor,
Postgres 16) on Windows 11
### Deployment
Docker-Compose
### Apache Airflow Provider(s)
_No response_
### Versions of Apache Airflow Providers
_No response_
### Official Helm Chart version
Not Applicable
### Kubernetes Version
_No response_
### Helm Chart configuration
_No response_
### Docker Image customizations
_No response_
### Anything else?
Every time, for any asset event emitted from inside a task loop. The same
`AssetEvent` component renders the event lists on the asset page, the Dag
overview, the dashboard, and the Dag run's and task instance's Asset Events
tabs, so the link is broken in all of them.
### Are you willing to submit PR?
- [x] Yes I am willing to submit a PR!
### Code of Conduct
- [x] I agree to follow this project's [Code of
Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)
--
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]