Sophia Ponte
04/22/2026, 6:38 PMMarvin
04/22/2026, 6:38 PMMarvin
04/22/2026, 6:40 PMon_completion hooks do not show up as separate nodes in the Prefect 3 UI graph.
In Prefect 3.x, the graph view is built from flow runs and task runs. Hooks run inline when the task enters a state like Completed, but they are not modeled as their own task/flow run objects, so there’s nothing for the graph to render as a node.
A few practical alternatives:
1. Log from the hook
If you want visibility in the task run details, logging is the simplest option.
from prefect import task
from prefect.logging.loggers import task_run_logger
def completion_hook(task, task_run, state):
logger = task_run_logger(task_run, task)
<http://logger.info|logger.info>(f"Completion hook ran for task={task.name} state={state.name}")
@task(on_completion=[completion_hook])
def my_task():
return 42
2. Emit an event
If you want something more observable / automation-friendly, emit a custom event from the hook.
from prefect import task
from prefect.events import emit_event
def completion_hook(task, task_run, state):
emit_event(
event="my-task.completion-hook-ran",
resource={"prefect.resource.id": f"prefect.task-run.{task_run.id}"},
payload={
"task_name": task.name,
"state": state.name,
},
)
@task(on_completion=[completion_hook])
def my_task():
return 42
3. Make the hook logic a real task instead
If you specifically want graph visibility, the supported pattern is to move that logic into an actual downstream task in the flow. Then it becomes a normal task run and will appear in the graph.
from prefect import flow, task
@task
def main_task():
return 42
@task
def completion_step(result):
print(f"Post-processing result: {result}")
@flow
def my_flow():
result = main_task()
completion_step(result)
That’s the best option if the post-completion logic is important enough that you want:
* graph visibility
* retries
* separate state tracking
* caching / concurrency controls
* clearer auditability
So the rule of thumb is:
* use hooks for lightweight side effects
* use tasks for anything you want represented in orchestration/UI
Relevant docs:
* State change hooks
* Flows
* Tasks
* Events
If you want, I can also show you how to convert an existing on_completion hook pattern into a graph-visible downstream task while preserving the same behavior.