Skip to main content
States are rich objects that contain information about the status of a particular task run or flow run. You can learn many things about a task or flow by examining its current state or the history of its states. For example, you might observe that a given run:
  • is Scheduled to run for the third time in an hour
  • is Late because the associated worker process is not responding
  • was Scheduled to run, but later Cancelled via button in the UI
  • was Cached from a previous run instead of Running again
  • has Completed after Running among its fellow task runs
  • ended up Failed because Claude left pydantic 1.x syntax in your code
  • ended up Crashed because you Ctrl-C’d the process
Only runs have states: Flows and tasks are templates that describe what a system does; only when we run the system does it also take on a state.

State types

Prefect states have names and types. A state’s name is often, but not always, synonymous with its type. For example, a task run that is running for the first time has a state with the name Running and the type RUNNING. However, if the task retries, that same task run will have the name Retrying and the type RUNNING. State types drive orchestration logic, whereas state names provide visual bookkeeping. The full list of states and state types includes:

State transitions

When a flow run changes states, you can often tell if it is behaving normally or not. Here are some common state transitions and what they mean:

Final state determination

The final state of a flow or task run depends on a number of factors; generally speaking there are three categories of terminal states:
  • COMPLETED: a run in any COMPLETED state did not encounter any errors or exceptions and returned successfully
  • FAILED: a run in any FAILED state encountered an error during execution, such as a raised exception
  • CRASHED: a run in any CRASHED state was interrupted by an OS signal such as a KeyboardInterrupt or an unexpected SIGTERM
The flow of state transitions can be visualized here:
States are represented by their name, with boxes behind states clarifying their underlying type. Dotted lines lead to terminal states.

Task return values

A task will be placed into a Completed state if it returns any Python object, with one exception: if a task explicitly returns a Prefect Failed state, the task will be marked Failed.
You can also access state objects directly within a flow through the return_state flag:
Returning a State via return_state=True is useful when you want to conditionally respond to the terminal states of a task or flow. For example, if state.is_failed(): ....

Flow return values

The final state of a flow is determined by its return value. The following rules apply:
  • If an exception is raised directly in the flow function, the flow run is marked as FAILED.
  • If a flow returns a manually created state, it is used as the state of the final flow run. This allows for manual determination of final state.
  • If a flow returns an iterable of states, the presence of any FAILED state will cause the run to be marked as FAILED.
In any other situation in which the flow returns without error, it will be marked as COMPLETED.
If you manipulate states programmatically, you can create situations in which tasks within a flow can fail and not cause flow run failure. For example:
If state were returned from the flow function, the run would be marked as FAILED.

State change hooks

State change hooks execute code in response to client side changes in flow or task run states, enabling you to define actions for specific state transitions in a workflow. State hooks have the following signature:
State change hooks are versatile, allowing you to specify multiple state change hooks for the same state transition, or to use the same state change hook for different transitions.

Further reading