<@ULVA73B9P> I get a timeout error "Task run did n...
# ask-marvin
s
@Marvin I get a timeout error "Task run did not complete within None seconds" in tasks that have no timeout defined. How is that possible ? How can I fix it ?
m
thought for 94.7 seconds
Short answer: this message is misleading. In Prefect 3.x there was a bug where waiting on a future could raise a TimeoutError with the text “within None seconds” even when you didn’t set a timeout. It doesn’t mean you configured a timeout of None; it’s just how the error string was formatted in that version. What typically triggers it - Calling
future.result()
(or
aresult()
) on a background task created with
.delay()
while it hasn’t completed yet. If the wait ends early (e.g., no active task worker, connectivity issue, or other interruption), the error used to say “None seconds”. - Less commonly: transient API/server connectivity issues while waiting for completion. References - Issue: Task run did not complete within None seconds - Fix: Fix confusing “None seconds” timeout error message How to fix or work around it 1) Upgrade Prefect to the latest 3.x release. The message was fixed so you’ll get a clearer error (and recent versions include other robustness improvements).
Copy code
pip install -U prefect
2) If you’re using background tasks, make sure a task worker is actually running and subscribed before you call `.result()`: - Run the task worker (e.g., via
.serve()
in a separate process) so your delayed tasks can be picked up. - Example:
Copy code
from prefect import task

@task
def background_task():
    return "done"

# In one process
if __name__ == "__main__":
    background_task.serve()  # runs the task worker
Then in your flow/process:
Copy code
f = background_task.delay()
res = f.result(timeout=600)  # optionally set an explicit timeout
3) If you truly want fire-and-forget, don’t call
.result()
at all:
Copy code
background_task.delay()  # enqueue and move on
4) If you need to wait indefinitely,
result()
with no timeout should block; if you still see errors after upgrading, try an explicit timeout and share logs:
Copy code
res = f.result(timeout=600)
Can you share: - Your Prefect version (
prefect version
) - Whether you’re using `.delay()`/background tasks vs. normal in-flow tasks - A minimal snippet of how you call
.result()
and how/where your worker is running With that, I can give more targeted guidance.
s
@Marvin • I'm using version
3.4.14
of prefect • Indeed I'm using background tasks and calling delay() followed by
result_async()
to await results in parallel:
Copy code
parallel_tasks = [
        import_table_parallel.with_options(
            name=f"import_table_{table}",
        ).delay(table)
        for table, json_fields in tables
    ]

    # Wait for all results of parallel tasks
    results = await asyncio.gather(
        *[task.result_async(raise_on_failure=False) for task in parallel_tasks]
    )
m
thought for 269.7 seconds
Thanks for the details — that helps a lot. You’re on 3.4.14 and using background tasks with
.delay()
then awaiting with
.result_async()
in parallel. There’s a known bug in this area where a wait can raise a TimeoutError with the confusing text “within None seconds” even when no timeout was set. It’s just the message format; it does not mean you configured a timeout of
None
. - Issue: Task run did not complete within None seconds - Fix (message clarity): Fix confusing “None seconds” timeout error message Important:
raise_on_failure=False
will not suppress a TimeoutError — that flag only affects raising when the task run ends in a failure state. A TimeoutError is thrown by the “waiting” layer itself. What to do next 1) Upgrade to the latest Prefect 3.x This includes the clearer error message and other robustness improvements.
Copy code
pip install -U prefect
2) Double‑check your background task worker is running and subscribed before you call
.delay()
Make sure the worker process for
import_table_parallel
is already up and connected to the same API as your flow process, e.g.:
Copy code
from prefect import task

@task
def import_table_parallel(table):
    ...

if __name__ == "__main__":
    # run this in a separate process/terminal before starting the flow
    import_table_parallel.serve()
If the worker isn’t running or can’t reach the API,
result_async()
may give you a wait error even though no explicit timeout was set. 3) Add an explicit timeout while you diagnose and don’t let a single wait cancel the whole group This gives you clearer control and visibility over which tasks are slow or not being picked up. Also set
return_exceptions=True
on
gather
so one exception doesn’t cancel the rest.
Copy code
results = await asyncio.gather(
    *[
        f.result_async(timeout=600, raise_on_failure=False)
        for f in parallel_tasks
    ],
    return_exceptions=True,
)
Then inspect any exceptions in
results
to see which future(s) are timing out. You can also log
f.id
and check the run in the UI to confirm it’s getting picked up. 4) If you don’t need distributed/background execution, consider in‑flow parallelism If your goal is just parallel CPU work on the same machine, using a task runner avoids the background worker path: - Process-based: ProcessPoolTaskRunner - Thread-based: ThreadPoolTaskRunner Docs: Task runners in Prefect 3 5) If it still happens after upgrading Please share: - How you start the worker (code snippet/logs) and confirm it’s running before the flow kicks off - Whether you’re on Prefect Cloud or self-hosted server - The exact stack trace of the TimeoutError from the flow side - One example task run ID that “timed out” along with the worker logs for that run I can help pinpoint whether it’s a worker subscription/visibility issue, a connectivity blip, or something else in your setup.
s
@Marvin Is there a default timeout for background tasks ?
m
thought for 21.3 seconds
Short answer: no. There is no default timeout for background tasks in Prefect 3.x. - Task runtime:
@task(timeout_seconds=...)
defaults to
None
(no engine-enforced timeout) if you don’t set it. See API: https://docs-3.prefect.io/v3/api-ref/python/prefect-tasks/#prefect.tasks.task - Waiting for results:
future.result_async(timeout=...)
also defaults to
None
, meaning it will wait indefinitely unless you supply a timeout. See API: https://docs-3.prefect.io/v3/api-ref/python/prefect-futures/ If you’re seeing “did not complete within None seconds,” that’s a known confusing error message when waiting on background tasks; it’s not indicating that a hidden timeout was applied. Details: issue and fix PR. If you want a guardrail, set an explicit timeout when awaiting:
Copy code
results = await asyncio.gather(
    *[f.result_async(timeout=600, raise_on_failure=False) for f in parallel_tasks],
    return_exceptions=True,
)
Otherwise, with the defaults, Prefect won’t time out your background tasks or the wait for their results.