Under which category would you file this issue?
Airflow Core
Apache Airflow version
3.3.0
What happened and how to reproduce it?
If "Other Airflow 2/3 version" selected, which one?
No response
What happened?
On a PostgreSQL metadata database, the UI DAG run stats endpoint returns HTTP 500 for every request:
GET /ui/dags/{dag_id}/dagRuns/{dag_run_id}/stats
[error] Exception in ASGI application [uvicorn.error] loc=httptools_impl.py:426
Traceback (most recent call last):
...
File ".../airflow/api_fastapi/core_api/routes/ui/dag_runs.py", line 63, in get_dag_run_stats
File ".../airflow/api_fastapi/core_api/services/ui/dag_run.py", line 55, in compute_duration_stats
File ".../airflow/api_fastapi/core_api/services/ui/dag_run.py", line 50, in _percentile
TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float'
Root cause is a type mismatch between the query and the helper that consumes it.
get_dag_run_stats (core_api/routes/ui/dag_runs.py) selects the SQL expression form of the duration:
durations = [
d
for d in session.scalars(
select(DagRun.duration.expression)
.where(...)
.limit(100)
)
if d is not None
]
return DagRunStatsResponse(duration=compute_duration_stats(durations))
DagRun.duration.expression (models/dagrun.py) is dialect dependent:
- PostgreSQL:
func.extract("epoch", cls.end_date - cls.start_date) -> SQL numeric -> psycopg returns decimal.Decimal
- MySQL:
func.timestampdiff(SECOND, ...) -> integer
- SQLite:
(julianday(end) - julianday(start)) * 86400 -> float
compute_duration_stats is annotated durations: list[float] and mixes the values with plain floats:
def _percentile(p: float) -> float:
idx = (len(sorted_d) - 1) * p / 100
lo = int(idx)
hi = min(lo + 1, len(sorted_d) - 1)
return sorted_d[lo] + (sorted_d[hi] - sorted_d[lo]) * (idx - lo)
With Decimal values, Decimal * float raises TypeError. This is not limited to the interpolating case: with a single completed run, idx - lo is 0.0 and Decimal(0) * 0.0 raises as well, so any DAG that has at least one completed run fails.
Impact is limited to this endpoint, which the UI calls when a DAG's run stats are displayed. Scheduling, task execution and the public API are unaffected.
What you think should happen instead?
The endpoint should return duration statistics regardless of the metadata database backend.
Coercing at the query boundary keeps compute_duration_stats matching its list[float] annotation:
durations = [float(d) for d in session.scalars(...) if d is not None]
Alternatively _percentile could operate on Decimal consistently, but the boundary cast looks closer to the intent of the existing type hints.
How to reproduce
- Deploy Airflow 3.3.0 with a PostgreSQL metadata database.
- Let at least one DAG run reach
success or failed.
- Call
GET /ui/dags/{dag_id}/dagRuns/{dag_run_id}/stats, or open the DAG in the UI so the stats panel loads.
- The request fails with HTTP 500 and the traceback above.
Not reproducible on SQLite or MySQL, where the same expression yields float / int.
Operating System
Debian GNU/Linux 12 (bookworm) — official apache/airflow:3.3.0 image
Versions of Apache Airflow Providers
No response
Deployment
Official Apache Airflow Helm Chart
Deployment details
Helm chart 1.22.0 with defaultAirflowTag / airflowVersion pinned to 3.3.0, KubernetesExecutor and CeleryExecutor deployments both affected, PostgreSQL 17.9 (Amazon RDS), Python 3.13.
Anything else?
Occurs on every request to the endpoint once a DAG has completed runs.
Are you willing to submit PR?
Code of Conduct
What you think should happen instead?
No response
Operating System
No response
Deployment
None
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?
No response
Are you willing to submit PR?
Code of Conduct
Under which category would you file this issue?
Airflow Core
Apache Airflow version
3.3.0
What happened and how to reproduce it?
If "Other Airflow 2/3 version" selected, which one?
No response
What happened?
On a PostgreSQL metadata database, the UI DAG run stats endpoint returns HTTP 500 for every request:
GET /ui/dags/{dag_id}/dagRuns/{dag_run_id}/statsRoot cause is a type mismatch between the query and the helper that consumes it.
get_dag_run_stats(core_api/routes/ui/dag_runs.py) selects the SQL expression form of the duration:DagRun.duration.expression(models/dagrun.py) is dialect dependent:func.extract("epoch", cls.end_date - cls.start_date)-> SQLnumeric-> psycopg returnsdecimal.Decimalfunc.timestampdiff(SECOND, ...)-> integer(julianday(end) - julianday(start)) * 86400-> floatcompute_duration_statsis annotateddurations: list[float]and mixes the values with plain floats:With
Decimalvalues,Decimal * floatraisesTypeError. This is not limited to the interpolating case: with a single completed run,idx - lois0.0andDecimal(0) * 0.0raises as well, so any DAG that has at least one completed run fails.Impact is limited to this endpoint, which the UI calls when a DAG's run stats are displayed. Scheduling, task execution and the public API are unaffected.
What you think should happen instead?
The endpoint should return duration statistics regardless of the metadata database backend.
Coercing at the query boundary keeps
compute_duration_statsmatching itslist[float]annotation:Alternatively
_percentilecould operate onDecimalconsistently, but the boundary cast looks closer to the intent of the existing type hints.How to reproduce
successorfailed.GET /ui/dags/{dag_id}/dagRuns/{dag_run_id}/stats, or open the DAG in the UI so the stats panel loads.Not reproducible on SQLite or MySQL, where the same expression yields
float/int.Operating System
Debian GNU/Linux 12 (bookworm) — official
apache/airflow:3.3.0imageVersions of Apache Airflow Providers
No response
Deployment
Official Apache Airflow Helm Chart
Deployment details
Helm chart 1.22.0 with
defaultAirflowTag/airflowVersionpinned to 3.3.0, KubernetesExecutor and CeleryExecutor deployments both affected, PostgreSQL 17.9 (Amazon RDS), Python 3.13.Anything else?
Occurs on every request to the endpoint once a DAG has completed runs.
Are you willing to submit PR?
Code of Conduct
What you think should happen instead?
No response
Operating System
No response
Deployment
None
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?
No response
Are you willing to submit PR?
Code of Conduct