Nicholas Pini
05/04/2026, 3:41 PMpython -m prefect.engine instead of prefect flow-run execute, are there going to be considerable differences in behaviour? For example, are heartbeat events generated and sent correctly?Marvin
05/04/2026, 3:41 PMMarvin
05/04/2026, 3:44 PMpython -m prefect.engine <flow-run-id> directly, you are still going through the same core engine code that emits flow-run heartbeats. So for your specific question: heartbeat events should still be generated and sent.
What’s different is everything around the engine process.
Heartbeat behavior
- Both paths ultimately run the flow via the engine codepath.
- Heartbeats are emitted by the engine itself, not uniquely by prefect flow-run execute.
- So python -m prefect.engine should still produce prefect.flow-run.heartbeat events, assuming:
- the run is associated with a reachable API / Cloud workspace
- event delivery is configured normally
- the process stays alive long enough to emit them
Important differences
prefect flow-run execute is not just a thin alias. It wraps engine execution with extra orchestration behavior, including things like:
- subprocess setup/environment management
- cancellation/control-channel handling
- runner/worker-managed lifecycle behavior
- some hook/crash handling responsibilities being managed outside the engine process
So the main distinction is:
- python -m prefect.engine = just run the engine directly
- prefect flow-run execute = run the engine in the way Prefect expects workers/runners to launch it
That means direct engine execution may differ in:
- cancellation behavior
- control-plane integration
- who owns terminal-state/crash handling
- environment variables injected before startup
Heartbeat frequency caveat
One concrete difference: prefect flow-run execute can set up heartbeat-related environment/config for the subprocess it launches. If you invoke python -m prefect.engine yourself, then heartbeat frequency will come from your existing settings/env instead of whatever the executor would have injected.
So if you compare them and see different timing, that’s likely why.
Recommendation
- For normal orchestration / production worker behavior: use prefect flow-run execute
- For debugging or special CI/manual execution: python -m prefect.engine is usually fine, and heartbeats should still work
I would not expect “missing heartbeats” purely because you used python -m prefect.engine, unless something about the surrounding environment/config differs.
CLI reference:
- `prefect flow-run execute`: verified via CLI help
If you want, I can also dig into:
- the exact heartbeat event type/payload
- how to confirm heartbeat delivery in Cloud/API logs
- whether this differs specifically in your Prefect 3.4.9 environmentNicholas Pini
05/05/2026, 7:19 AMprefect flow-run execute starts flows which send heartbeat events, python -m prefect.engine starts flows which DO NOT send heartbeat events. Nothing else in the configuration changedMarvin
05/05/2026, 7:19 AMMarvin
05/05/2026, 7:22 AMprefect flow-run execute does extra setup before launching the engine subprocess. In particular, it creates and manages flow-run execution context around the subprocess launch. That path includes explicit event-emitter/client lifecycle management and startup wiring that direct python -m prefect.engine does not do.
By contrast, python -m prefect.engine is a much thinner entrypoint. It runs the engine, but it does not establish the same surrounding executor context first.
So although both paths eventually run the same core engine code, the CLI path has extra orchestration scaffolding that can be the difference between:
- heartbeat events actually being delivered
- heartbeat events being skipped, not initialized, or not flushed correctly
Most likely reason
The strongest explanation from the code is:
- prefect flow-run execute runs inside a FlowRunExecutorContext
- that context explicitly creates/manages an EventEmitter
- python -m prefect.engine does not
So the direct module path is relying on more implicit/lazy initialization for events, while the CLI path eagerly sets up the event plumbing.
There is also a heartbeat-frequency/config difference:
- the executor path can inject PREFECT_FLOWS_HEARTBEAT_FREQUENCY
- direct python -m prefect.engine only sees whatever is already in your environment/settings
If that value is absent or resolves differently, heartbeats may not run at all.
Why your “nothing else changed” result still makes sense
Even if you didn’t change config, the launcher path itself changes process setup:
- environment variables passed to the subprocess
- control/cancellation wiring
- event-emitter lifecycle
- initialization order
So “same deployment, different launcher” can absolutely produce different heartbeat behavior.
Practical takeaway
If you need worker-equivalent behavior, especially around:
- heartbeats
- cancellation
- state/lifecycle integration
then use:
bash
prefect flow-run execute <flow-run-id>
not:
bash
python -m prefect.engine <flow-run-id>
What I’d treat as the safest statement
I would not rely on python -m prefect.engine as a supported drop-in replacement for prefect flow-run execute if heartbeat delivery matters.
Relevant reference
There’s already a related community discussion on this exact distinction:
Difference between `prefect.engine` and `prefect flow-run execute`
If you want, I can do one more pass specifically for Prefect 3.4.9 and try to pinpoint:
- the exact env vars prefect flow-run execute adds
- whether setting those manually makes python -m prefect.engine send heartbeats in your setup
- a minimal reproduction you can use in CodeBuild/CI