Ok my previous thinking was wrong. I dove into this with
@Jean Luciano, one of our solutions engineers and here is what we found. I think we are consistent with your findings, and then I’ll add some explanation.
If you click cancel in the UI, it will hit the State Handler that contains:
def mystatehandler(flow, old_state, new_state):
if isinstance(new_state, Cancelled):
and also:
def kill_keda_job_callback(flow, old_state, new_state):
if new_state.is_finished():
gets triggered because of the finished state.
When we set the state to Failed in the UI, there are two possible outcomes. The first is if there are still downstream tasks, the Flow gets confused and there are Client errors. The second is if you mark as FAILED during a terminal task. In this case, that last task may still succeed causing the state to succeed. State handlers for
is_failed()
are not hit in this case
This is because setting the state in the UI and using the Cancel button take two code paths. Setting the state in the UI is a direct modification of state in the Prefect database. It does not touch the Flow process. Using the Cancel button goes into the Flow process and tries to terminate it. Because it is done in the Flow process, the Flow can still run the state handlers while exiting.
So the summary is it’s not by design but you are right that Cancel works differently and setting state in the UI does not