Olve Heitmann
10/24/2025, 9:43 PMMarvin
10/24/2025, 9:44 PMMarvin
10/24/2025, 9:50 PMfrom datetime import datetime, timedelta, timezone
from typing import Sequence
from prefect import flow
from prefect.client.orchestration import get_client
from prefect.client.schemas.filters import (
FlowRunFilter,
FlowRunFilterExpectedStartTime,
FlowRunFilterStateType,
FlowRunFilterStateName,
FlowRunFilterDeploymentId,
FlowRunFilterWorkQueueName,
)
from prefect.client.schemas.objects import StateType
@flow
def find_missed_runs(
days: int = 7,
deployment_ids: Sequence[str] | None = None,
work_queue_names: Sequence[str] | None = None,
only_late: bool = True, # if False, returns all scheduled states in the window
limit: int = 1000,
) -> list[str]:
now = datetime.now(timezone.utc)
window_start = now - timedelta(days=days)
# Build filters
expected_time_filter = FlowRunFilterExpectedStartTime(after_=window_start, before_=now)
# Option A: specifically “Late” runs (recommended for “missed”)
state_filter = FlowRunFilterStateName(any_=["Late"]) if only_late else None
# Option B: any scheduled runs in the window (includes Scheduled/Late/AwaitingRetry/etc.)
state_type_filter = None if only_late else FlowRunFilterStateType(any_=[StateType.SCHEDULED])
dep_filter = FlowRunFilterDeploymentId(any_=list(deployment_ids)) if deployment_ids else None
wq_filter = FlowRunFilterWorkQueueName(any_=list(work_queue_names)) if work_queue_names else None
filters = FlowRunFilter(
expected_start_time=expected_time_filter,
state_name=state_filter,
state_type=state_type_filter,
deployment_id=dep_filter,
work_queue_name=wq_filter,
)
with get_client(sync_client=True) as client:
runs = client.read_flow_runs(flow_run_filter=filters, limit=limit)
# Log a quick summary
for r in runs:
print(
f"{r.id} | {r.name} | expected_start={r.expected_start_time} | "
f"state={r.state_name} | deployment_id={r.deployment_id}"
)
# Return the IDs (adjust to return the whole objects if you prefer)
return [r.id for r in runs]
if __name__ == "__main__":
find_missed_runs()
Notes:
- Set only_late=True to capture just the missed runs. If you set it to False, you’ll get all scheduled runs in the time window (Scheduled, Late, AwaitingRetry, etc.).
- You can scope to specific deployments (deployment_ids=[...]) or work queues (work_queue_names=[...]) if you don’t want to search everything.
Docs for reference:
- States in Prefect 3.x (what “Late” means): States
- Python client usage: API Client
- Filters API reference: Filters API
Would you like this scoped to a particular deployment, flow, or work queue, or should it scan your entire workspace? Also, what would you like to do with the results—just list them, or requeue/reschedule them automatically?Olve Heitmann
10/24/2025, 9:52 PMMarvin
10/24/2025, 9:52 PMMarvin
10/24/2025, 9:56 PMOlve Heitmann
10/27/2025, 8:08 PMMarvin
10/27/2025, 8:08 PMMarvin
10/27/2025, 8:13 PMfrom datetime import datetime, timezone, timedelta
from prefect import flow
from prefect.runtime import flow_run
@flow
def my_flow(max_lateness_minutes: int = 30):
sched = flow_run.scheduled_start_time()
if sched and datetime.now(timezone.utc) - sched > timedelta(minutes=max_lateness_minutes):
print("Skipping run because it’s too late")
return
# normal work here
References:
- States: States in Prefect 3.x
- Workers/polling: Workers
- Automations: Creating automationsOlve Heitmann
10/27/2025, 8:15 PMMarvin
10/27/2025, 8:15 PMMarvin
10/27/2025, 8:16 PMOlve Heitmann
10/27/2025, 8:18 PMMarvin
10/27/2025, 8:18 PMMarvin
10/27/2025, 8:18 PMfrom datetime import datetime, timedelta, timezone
from typing import Sequence
from prefect import flow
from prefect.client.orchestration import get_client
from prefect.client.schemas.filters import (
FlowRunFilter,
FlowRunFilterExpectedStartTime,
FlowRunFilterStateType,
FlowRunFilterStateName,
)
from prefect.client.schemas.objects import StateType
@flow
def find_missed_runs(
days: int = 7,
state_names: Sequence[str] | None = ("Late",), # None => all SCHEDULED states
limit: int = 1000,
):
now = datetime.now(timezone.utc)
window_start = now - timedelta(days=days)
expected_time = FlowRunFilterExpectedStartTime(after_=window_start, before_=now)
# If state_names is provided, filter by those names; otherwise include all SCHEDULED states
state_name_filter = FlowRunFilterStateName(any_=list(state_names)) if state_names else None
state_type_filter = None if state_names else FlowRunFilterStateType(any_=[StateType.SCHEDULED])
flt = FlowRunFilter(
expected_start_time=expected_time,
state_name=state_name_filter,
state_type=state_type_filter,
)
with get_client(sync_client=True) as client:
runs = client.read_flow_runs(flow_run_filter=flt, limit=limit)
for r in runs:
print(f"{r.id} | {r.name} | expected={r.expected_start_time} | state={r.state_name}")
return runs
Examples:
- Only missed runs: keep default state_names=("Late",)
- All scheduled states in the window: state_names=None
- A custom subset (e.g., Late and AwaitingRetry): state_names=("Late", "AwaitingRetry")
State reference: States in Prefect 3.x
Which behavior do you prefer: only Late, or a broader view of all scheduled-but-not-started runs?