Skip to content

PydanticSerializationError on deadline alerts UI endpoint for Dags overview and Dag run #69245

Description

@TJaniF

Under which category would you file this issue?

Airflow Core

Apache Airflow version

v3-3-test

What happened and how to reproduce it?

I was looking for the deadline alerts UI additions described in #62195 and could not find them. Inspecting the page I see 500 errors relating on GET /ui/dags/{dag_id}/deadlineAlerts:

Image

The API server logs show a pydantic serialization error:

2026-07-02T10:33:27.403495Z [error    ] Exception in ASGI application
 [uvicorn.error] loc=httptools_impl.py:426
Traceback (most recent call last):
  File "/usr/python/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 421, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/usr/python/lib/python3.10/site-packages/fastapi/applications.py", line 1159, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/python/lib/python3.10/site-packages/starlette/applications.py", line 90, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/python/lib/python3.10/site-packages/starlette/middleware/errors.py", line 186, in __call__
    raise exc
  File "/usr/python/lib/python3.10/site-packages/starlette/middleware/errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "/opt/airflow/airflow-core/src/airflow/api_fastapi/common/http_access_log.py", line 109, in __call__
    await self.app(scope, receive, capture_send)
  File "/usr/python/lib/python3.10/site-packages/starlette/middleware/gzip.py", line 29, in __call__
    await responder(scope, receive, send)
  File "/usr/python/lib/python3.10/site-packages/starlette/middleware/gzip.py", line 130, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/python/lib/python3.10/site-packages/starlette/middleware/gzip.py", line 46, in __call__
    await self.app(scope, receive, self.send_with_compression)
  File "/usr/python/lib/python3.10/site-packages/starlette/middleware/base.py", line 193, in __call__
    response = await self.dispatch_func(request, call_next)
  File "/opt/airflow/airflow-core/src/airflow/api_fastapi/auth/middlewares/refresh_token.py", line 68, in dispatch
    response = await call_next(request)
  File "/usr/python/lib/python3.10/site-packages/starlette/middleware/base.py", line 168, in call_next
    raise app_exc from app_exc.__cause__ or app_exc.__context__
  File "/usr/python/lib/python3.10/site-packages/starlette/middleware/base.py", line 144, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/usr/python/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 63, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/usr/python/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    raise exc
  File "/usr/python/lib/python3.10/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
    await app(scope, receive, sender)
  File "/usr/python/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
  File "/usr/python/lib/python3.10/site-packages/starlette/routing.py", line 660, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/python/lib/python3.10/site-packages/starlette/routing.py", line 680, in app
    await route.handle(scope, receive, send)
  File "/usr/python/lib/python3.10/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/usr/python/lib/python3.10/site-packages/fastapi/routing.py", line 134, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/usr/python/lib/python3.10/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    raise exc
  File "/usr/python/lib/python3.10/site-packages/starlette/_exception_handler.py", line 42, in wrapped_app
    await app(scope, receive, sender)
  File "/usr/python/lib/python3.10/site-packages/fastapi/routing.py", line 120, in app
    response = await f(request)
  File "/usr/python/lib/python3.10/site-packages/fastapi/routing.py", line 695, in app
    content = await serialize_response(
  File "/usr/python/lib/python3.10/site-packages/fastapi/routing.py", line 306, in serialize_response
    return serializer(
  File "/usr/python/lib/python3.10/site-packages/fastapi/_compat/v2.py", line 231, in serialize_json
    return self._type_adapter.dump_json(
  File "/usr/python/lib/python3.10/site-packages/pydantic/type_adapter.py", line 677, in dump_json
    return self.serializer.to_json(
pydantic_core._pydantic_core.PydanticSerializationError: Error serializing to JSON: ValidationError: 1 validation error for ValidatorIterator
0.interval
  Input should be a valid number [type=float_type, input_value={'__classname__': 'dateti..._': 2, '__data__': 10.0}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.13/v/float_type

The deadlines show up correctly as missed in the main deadlines list:
Image

My two example Dags:

from airflow.sdk import dag, task
from airflow.sdk.definitions.deadline import (
    SyncCallback,
    AsyncCallback,
    DeadlineAlert,
    DeadlineReference,
)

from datetime import timedelta


async def custom_async_callback(**kwargs):
    """Handle deadline violation with custom logic."""
    print(f"Deadline exceeded for Dag {kwargs.get('dag_id')}!")
    print(f"Alert type: {kwargs.get('alert_type')}")


def sync_callback(**kwargs):
    """Handle deadline violation with custom logic."""
    print(f"Deadline exceeded for Dag {kwargs.get('dag_id')}!")
    print(f"Alert type: {kwargs.get('alert_type')}")


@dag(
    deadline=DeadlineAlert(
        reference=DeadlineReference.DAGRUN_QUEUED_AT,
        interval=timedelta(seconds=10),
        callback=SyncCallback(
            sync_callback,
            kwargs={"alert_type": "time_exceeded", "dag_id": "deadline_alerts_sync_callback"},
        ),
    ),
)
def deadline_alerts_sync_callback():
    @task
    def print_hello():
        import time

        time.sleep(15)
        print("Hello, World!")

    print_hello()


deadline_alerts_sync_callback()


@dag(
    deadline=DeadlineAlert(
        reference=DeadlineReference.DAGRUN_QUEUED_AT,
        interval=timedelta(seconds=10),
        callback=AsyncCallback(
            custom_async_callback,
            kwargs={"alert_type": "time_exceeded", "dag_id": "deadline_alerts_async_callback"},
        ),
    ),
)
def deadline_alerts_async_callback():
    @task
    def print_hello():
        import time

        time.sleep(15)
        print("Hello, World!")

    print_hello()


deadline_alerts_async_callback()

Repro:

  1. Run either one of the above Dags to create a missed deadline.
  2. Verify that there is a missed deadline in Browse -> deadlines
  3. Click on the Dag that ran, select the Overview tab
  4. Inspect the page and see the error in the console and the error in the api server

What you think should happen instead?

The UI additions from #62195 should be visible.

Operating System

macOs Tahoe 26.5.1

Deployment

Other

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?

  • Yes I am willing to submit a PR!

Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:APIAirflow's REST/HTTP APIarea:UIRelated to UI/UX. For Frontend Developers.kind:bugThis is a clearly a bug

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions