Repository: apache/airflow

Issue:
Dynamic task mapping does not correctly handle depends_on_past
### Apache Airflow version

Other Airflow 2 version (please specify below)

### What happened

Using Airflow 2.4.2.

I've got a task that retrieves some filenames, which then creates dynamically mapped tasks to move the files, one per task.
I'm using a similar task across multiple DAGs. However, task mapping fails on some DAG runs: it inconsistently happens per DAG run, and some DAGs do not seem to be affected at all. These seem to be the DAGs where no task was ever mapped, so that the mapped task instance ended up in a Skipped state.

What happens is that multiple files will be found, but only a single dynamically mapped task will be created. This task never starts and has map_index of -1. It can be found under the "List instances, all runs" menu, but says "No Data found." under the "Mapped Tasks" tab.

When I press the "Run" button when the mapped task is selected, the following error appears:

```
Could not queue task instance for execution, dependencies not met: Previous Dagrun State: depends_on_past is true for this task's DAG, but the previous task instance has not run yet., Task has been mapped: The task has yet to be mapped!
```

The previous task *has* run however. No errors appeared in my Airflow logs.

### What you think should happen instead

The appropriate amount of task instances should be created, they should correctly resolve the ```depends_on_past``` check and then proceed to run correctly.

### How to reproduce

This DAG reliably reproduces the error for me. The first set of mapped tasks succeeds, the subsequent ones do not.

```python
from airflow import DAG
from airflow.decorators import task
import datetime as dt

from airflow.operators.python import PythonOperator

@task
def get_filenames_kwargs():
    return [
        {"file_name": i}
        for i in range(10)
    ]

def print_filename(file_name):
    print(file_name)

with DAG(
        dag_id="dtm_test",
        start_date=dt.datetime(2022, 12, 10),
        default_args={
            "owner": "airflow",
            "depends_on_past": True,
        },
        schedule="@daily",
) as dag:
    get_filenames_task = get_filenames_kwargs.override(task_id="get_filenames_task")()

    print_filename_task = PythonOperator.partial(
        task_id="print_filename_task",
        python_callable=print_filename,
    ).expand(op_kwargs=get_filenames_task)

    # Perhaps redundant
    get_filenames_task >> print_filename_task
```


### Operating System

Amazon Linux 2

### Versions of Apache Airflow Providers

_No response_

### Deployment

Other Docker-based deployment

### Deployment details

_No response_

### Anything else

_No response_

### Are you willing to submit PR?

- [ ] 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)



Discussion:

Resolving patch:
diff --git a/airflow/ti_deps/deps/prev_dagrun_dep.py b/airflow/ti_deps/deps/prev_dagrun_dep.py
--- a/airflow/ti_deps/deps/prev_dagrun_dep.py
+++ b/airflow/ti_deps/deps/prev_dagrun_dep.py
@@ -70,7 +70,7 @@ def _get_dep_statuses(self, ti: TI, session, dep_context):
             yield self._passing_status(reason="This task instance was the first task instance for its task.")
             return
 
-        previous_ti = last_dagrun.get_task_instance(ti.task_id, session=session)
+        previous_ti = last_dagrun.get_task_instance(ti.task_id, map_index=ti.map_index, session=session)
         if not previous_ti:
             if ti.task.ignore_first_depends_on_past:
                 has_historical_ti = (


Test patch:
diff --git a/tests/models/test_dagrun.py b/tests/models/test_dagrun.py
--- a/tests/models/test_dagrun.py
+++ b/tests/models/test_dagrun.py
@@ -2187,3 +2187,38 @@ def say_bye():
     assert tis["add_one__1"] == TaskInstanceState.SKIPPED
     assert tis["add_one__2"] == TaskInstanceState.SKIPPED
     assert dr.state == State.SUCCESS
+
+
+def test_mapped_task_depends_on_past(dag_maker, session):
+    with dag_maker(session=session):
+
+        @task(depends_on_past=True)
+        def print_value(value):
+            print(value)
+
+        print_value.expand_kwargs([{"value": i} for i in range(2)])
+
+    dr1: DagRun = dag_maker.create_dagrun(run_type=DagRunType.SCHEDULED)
+    dr2: DagRun = dag_maker.create_dagrun_after(dr1, run_type=DagRunType.SCHEDULED)
+
+    # print_value in dr2 is not ready yet since the task depends on past.
+    decision = dr2.task_instance_scheduling_decisions(session=session)
+    assert len(decision.schedulable_tis) == 0
+
+    # Run print_value in dr1.
+    decision = dr1.task_instance_scheduling_decisions(session=session)
+    assert len(decision.schedulable_tis) == 2
+    for ti in decision.schedulable_tis:
+        ti.run(session=session)
+
+    # Now print_value in dr2 can run
+    decision = dr2.task_instance_scheduling_decisions(session=session)
+    assert len(decision.schedulable_tis) == 2
+    for ti in decision.schedulable_tis:
+        ti.run(session=session)
+
+    # Both runs are finished now.
+    decision = dr1.task_instance_scheduling_decisions(session=session)
+    assert len(decision.unfinished_tis) == 0
+    decision = dr2.task_instance_scheduling_decisions(session=session)
+    assert len(decision.unfinished_tis) == 0