Madushan Pathirana
11/17/2025, 10:59 AMMarvin
11/17/2025, 10:59 AMMarvin
11/17/2025, 11:02 AMstate.name and your mapping does not contain that exact key (or you’re using [...] instead of .get()), you’ll get this KeyError.
Two solid fixes:
1) Keep mapping by state name but ensure the exact spellings are present and always use .get(...) with a default.
2) Prefer mapping by state type instead of name. It’s more robust because multiple names map to the same type (e.g., 'Running' and 'Retrying' are both RUNNING). Here’s a drop-in update that avoids KeyErrors and covers the common types:
from prefect import flow
from prefect.states import State
from prefect.client.schemas.objects import Flow, FlowRun # adjust imports if needed
def pipeline_alert(flow: Flow, flow_run: FlowRun, state: State):
# Prefer state.type.value for stable matching
state_type = state.type.value # e.g., 'RUNNING', 'CANCELLING', 'COMPLETED', etc.
emoji_by_type = {
'FAILED': ':warning:',
'CRASHED': ':warning:',
'RUNNING': ':rocket:',
'COMPLETED': ':white_check_mark:',
'CANCELLED': ':no_entry_sign:',
'CANCELLING': ':x:',
# Optional: cover more types
'SCHEDULED': ':calendar:',
'PENDING': ':hourglass_flowing_sand:',
'PAUSED': ':pause_button:',
}
state_emoji = emoji_by_type.get(state_type, ':question:')
subject = f"{state_emoji} {SOURCE.upper()} - {flow_run.name} - {state.name} - {APP_ENV.upper()}"
# Use real HTML so the link is clickable in email
body_html = f"""
<html>
<body>
<p>See <a href="{PREFECT_UI_URL.value()}/flow-runs/flow-run/{flow_run.id}">the flow run in the UI</a></p>
</body>
</html>
""".strip()
# For SES, use plain email addresses (no Slack mailto markup)
sender = "<mailto:reports@credilinq.ai|reports@credilinq.ai>"
to_addresses = ["<mailto:DataScienceTeam@credilinq.ai|DataScienceTeam@credilinq.ai>"]
ses = ses_client()
response = ses.send_email(
Source=sender,
Destination={'ToAddresses': to_addresses},
Message={
'Subject': {'Data': subject},
'Body': {'Html': {'Data': body_html}}
}
)
print(response)
Why this works:
- Uses state.type.value so you don’t need to remember the exact casing/spelling of names like 'Cancelling'.
- Provides a default emoji for unknown states, preventing KeyErrors.
- Fixes SES address formatting to plain emails (SES will reject <mailto:...> markup).
For reference on exact state names and types in Prefect 3.x, see:
- Concepts: States
- API Reference: prefect.states
If you’d rather keep name-based mapping, ensure you include 'Cancelling' (double L) and use .get(flow_state, ':question:') everywhere you access the mapping.