Russell Brooks
05/22/2026, 9:30 AMMarvin
05/22/2026, 9:30 AMMarvin
05/22/2026, 9:33 AMawait.
The specific coroutine name is a strong clue:
text
coroutine 'sync_compatible.<locals>.coroutine_wrapper.<locals>.ctx_call' was never awaited
That comes from Prefect’s @sync_compatible wrapper. In an async def flow, some Prefect APIs return a coroutine and must be awaited, even if the same API can be called without await in a sync flow.
Most likely cause
Inside your async flow, you have something like one of these:
python
@flow
async def my_flow():
x = Variable.get("foo") # should be: await Variable.get("foo")
b = Block.load("my-block") # should be: await Block.load("my-block")
Other Prefect APIs with this behavior can do the same thing.
A Prefect PR/discussion specifically calls out this pattern for Variable.get in sync vs async contexts:
clarify variables docs
Example from that PR:
python
@flow
async def af():
print(await Variable.get("answer"))
How to find the exact line
The warning often points at asyncio internals instead of your code, so the best trick is to turn it into an error:
bash
python -W error::RuntimeWarning your_script.py
That usually gives you a traceback to the offending line.
You can also grep your async flow for Prefect calls that are not awaited, especially:
- Variable.get(...)
- Block.load(...)
- other Prefect methods you’re calling directly inside async def
What to change
If your flow is async:
python
from prefect import flow
from prefect.variables import Variable
@flow
async def my_flow():
value = await Variable.get("foo")
print(value)
And similarly for blocks:
python
@flow
async def my_flow():
block = await Block.load("my-block-name")
Important nuance
In Prefect 3.x, some APIs are “dually contextual”:
- in def flows/tasks: call normally
- in async def flows/tasks: await them
So code that works fine in a sync flow can emit this warning when moved into an async flow.
What I found in Prefect’s implementation
This comes from Prefect’s sync_compatible utility, which returns a coroutine in async contexts. If that return value is not awaited, Python emits exactly the warning you’re seeing.
I didn’t find a GitHub discussion specifically for this exact warning string, but the behavior is consistent with the documented sync/async Variable.get pattern and the underlying implementation.
Useful links
- Variables sync/async clarification: PR 15233
- Related issue touching sync_compatible behavior/docs: Issue 16229
If you want, paste the async flow code (or even just the body of the flow), and I can help spot the exact call that needs await.Russell Brooks
05/22/2026, 9:37 AMMarvin
05/22/2026, 9:37 AMMarvin
05/22/2026, 9:38 AMuv run.
Use:
bash
uv run python -W error::RuntimeWarning your_script.py
If you normally run a module:
bash
uv run python -W error::RuntimeWarning -m your_module
If you want all runtime warnings promoted to errors during a Prefect invocation, you can also use the env var form:
bash
PYTHONWARNINGS=error::RuntimeWarning uv run python your_script.py
On Windows PowerShell:
powershell
$env:PYTHONWARNINGS="error::RuntimeWarning"
uv run python your_script.py
If you're launching via `prefect run`/`prefect deploy`/worker execution, it gets a little trickier because the warning setting needs to be present in the actual Python process running your flow. For local reproduction, uv run python ... is the easiest path.
If you want, I can help translate this to the exact command you use today to start the flow.Russell Brooks
05/22/2026, 9:40 AMMarvin
05/22/2026, 9:40 AMMarvin
05/22/2026, 9:40 AM-W error::RuntimeWarning didn’t surface anything new, that usually means one of two things:
1. the warning is emitted during shutdown / cleanup, where the traceback is not very helpful, or
2. the un-awaited coroutine is being created indirectly inside another call, not at an obvious Variable.get / Block.load line.
A few practical ways to narrow it down:
1. Enable asyncio debug mode too
Run with both:
bash
PYTHONASYNCIODEBUG=1 PYTHONWARNINGS=default uv run python your_script.py
Or:
bash
uv run python -X dev your_script.py
-X dev turns on extra runtime checks and often gives better asyncio diagnostics.
On PowerShell:
powershell
$env:PYTHONASYNCIODEBUG="1"
$env:PYTHONWARNINGS="default"
uv run python your_script.py
2. Look for “hidden” Prefect calls, not just load / get
The warning is from Prefect’s sync_compatible wrapper, so the culprit may be another Prefect API that behaves sync in def code and async in async def code.
Things to inspect in your async flow/tasks:
- calling a Prefect-decorated task but not awaiting the result
- task.submit(...) / task.map(...) patterns
- pause_flow_run, resume_flow_run, or other orchestration helpers
- block methods besides load
- variable / secret / artifact helpers
- anything returning a state or future that you then use like a plain value
For example, these can be suspicious depending on context:
python
result = my_task()
future = my_task.submit()
values = my_task.map(items)
Inside async flows, the exact correct pattern depends on whether you want direct execution, submitted futures, or mapped results.
3. Check async tasks too, not just the flow body
A missing await inside an @task async def ... can surface while the async flow is running, even if the flow itself looks correct.
4. Add temporary probes around suspicious calls
If the warning doesn’t point to a line, binary-search your flow:
python
@flow
async def my_flow():
print("before step 1")
await step_1()
print("after step 1")
print("before step 2")
x = await maybe_prefect_call()
print("after step 2", x)
Or around direct calls:
python
print("before foo")
result = foo(...)
print("after foo", type(result), result)
If you ever see the returned object is a coroutine, that’s your smoking gun.
You can also test explicitly:
python
import inspect
res = some_call(...)
print(type(res), inspect.iscoroutine(res))
If that prints True, it needed await.
5. Watch for helper functions wrapping Prefect APIs
Sometimes the missing await is hidden in your own utility layer:
python
def get_config():
return Variable.get("x") # bad if called from async context
Then later:
python
@flow
async def my_flow():
cfg = get_config()
At a glance the flow looks fine, but get_config() returned a coroutine.
In async code that helper would need to be:
python
async def get_config():
return await Variable.get("x")
6. The warning may come from import-time or setup code
If you have module-level code like:
python
MY_VAR = Variable.get("x")
MY_BLOCK = Block.load("y")
that can also behave oddly depending on where/how the module is imported.
7. Use tracemalloc for better warning origins
Sometimes Python will show where the coroutine was allocated if tracemalloc is enabled:
bash
uv run python -X tracemalloc=25 -W default your_script.py
Combined with PYTHONASYNCIODEBUG=1, this can help.
Most likely culprit pattern if “nothing obvious” is visible
The most common next-level cause is:
- a helper function that calls a sync-compatible Prefect API
- a task invocation pattern in async code
- a Prefect utility call buried in setup code, callbacks, hooks, or a custom class method
If you paste:
- the async flow
- any async tasks it calls
- any helper functions that touch Prefect APIs
I can usually spot the likely source pretty quickly.