Fabien Punin
11/30/2025, 2:28 PM07:01:17 AM
Error
Encountered exception during execution: AttributeError("'TaskRun' object has no attribute 'state'")
Traceback (most recent call last):
File "/app/.venv/lib/python3.11/site-packages/prefect/task_engine.py", line 1220, in initialize_run
validated_state=self.task_run.state,
^^^^^^^^^^^^^^^^^^^
File "/app/.venv/lib/python3.11/site-packages/pydantic/main.py", line 994, in __getattr__
raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}')
AttributeError: 'TaskRun' object has no attribute 'state'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/app/.venv/lib/python3.11/site-packages/prefect/flow_engine.py", line 1332, in run_context
yield self
File "/app/.venv/lib/python3.11/site-packages/prefect/flow_engine.py", line 1394, in run_flow_async
await engine.call_flow_fn()
File "/app/.venv/lib/python3.11/site-packages/prefect/flow_engine.py", line 1346, in call_flow_fn
result = await call_with_parameters(self.flow.fn, self.parameters)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/data_platform/internal_alerting/src/prefect/flows/internal_alerting.py", line 57, in internal_alerting
task_end_states = resolve_futures_to_states(tasks)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/.venv/lib/python3.11/site-packages/prefect/futures.py", line 643, in resolve_futures_to_states
future.wait()
File "/app/.venv/lib/python3.11/site-packages/prefect/futures.py", line 197, in wait
result = self._wrapped_future.result(timeout=timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 449, in result
return self.__get_result()
^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
raise self._exception
File "/usr/local/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/asyncio/runners.py", line 190, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/asyncio/base_events.py", line 654, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/app/.venv/lib/python3.11/site-packages/prefect/task_engine.py", line 1412, in run_task_async
async with engine.start(task_run_id=task_run_id, dependencies=dependencies):
File "/usr/local/lib/python3.11/contextlib.py", line 210, in __aenter__
return await anext(self.gen)
^^^^^^^^^^^^^^^^^^^^^
File "/app/.venv/lib/python3.11/site-packages/prefect/task_engine.py", line 1287, in start
async with self.initialize_run(
File "/usr/local/lib/python3.11/contextlib.py", line 210, in __aenter__
return await anext(self.gen)
^^^^^^^^^^^^^^^^^^^^^
File "/app/.venv/lib/python3.11/site-packages/prefect/task_engine.py", line 1258, in initialize_run
self.log_finished_message()
File "/app/.venv/lib/python3.11/site-packages/prefect/task_engine.py", line 251, in log_finished_message
display_state = repr(self.state) if PREFECT_DEBUG_MODE else str(self.state)
^^^^^^^^^^
File "/app/.venv/lib/python3.11/site-packages/prefect/task_engine.py", line 136, in state
if not self.task_run or not self.task_run.state:
^^^^^^^^^^^^^^^^^^^
File "/app/.venv/lib/python3.11/site-packages/pydantic/main.py", line 994, in __getattr__
raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}')
AttributeError: 'TaskRun' object has no attribute 'state'
And here is the flow code:
@flow(
result_storage=common_settings.PREFECT_RESULT_STORAGE_S3_BLOCK_NAME,
cache_result_in_memory=False,
on_cancellation=[
slack_notifier.send_flow_run_finished_slack_notification,
],
on_crashed=[slack_notifier.send_flow_run_finished_slack_notification],
log_prints=True,
)
async def internal_alerting() -> State | None:
logging = Logging(logging_level=common_settings.LOGGING_LEVEL)
logger = logging.get_logger()
app_container = AwsInternalAlertingAppContainer(
config=aws_internal_alerting_settings,
common_config=common_settings,
logging=logging,
)
alert_service = app_container.services.alert_service
configs = aws_internal_alerting_settings.CONFIGS
tasks = [
alert_service.process_alert.submit(cfg)
for cfg in configs
if cfg.is_active()
]
task_end_states = resolve_futures_to_states(tasks)
has_failures = has_failed_tasks(task_end_states)
if has_failures:
message = (
"Internal alerting failed. Please check the logs for more information."
)
else:
message = "Internal alerting completed successfully."
return await handle_flow_completion(
slack_client=slack_notifier,
logger=logger,
message=message,
is_success=not has_failures,
send_slack=True,
)Marvin
11/30/2025, 2:29 PMMarvin
11/30/2025, 2:37 PMTaskRun object hasn’t finished initializing its state attribute. We’ve seen it happen more often with lots of near-simultaneous .submit() calls. There’s an open issue tracking this exact error with a minimal repro and stack trace very similar to yours: Submitting many tasks simultaneously raises TaskRun AttributeError.
What you can do right now
- Upgrade Prefect to the latest 3.x
- Even though the release notes don’t explicitly call out this fix, staying current has reduced the frequency of these engine-side race conditions for many users.
- Latest notes: 3.6.4 release (full changelog: 3.6.3…3.6.4)
- Reduce the burstiness of task submissions
- Set a task runner with bounded concurrency so you don’t submit a very large number of tasks at once:
from prefect import flow
from prefect.task_runners import ThreadPoolTaskRunner
@flow(
task_runner=ThreadPoolTaskRunner(max_workers=8),
result_storage=common_settings.PREFECT_RESULT_STORAGE_S3_BLOCK_NAME,
cache_result_in_memory=False,
on_cancellation=[slack_notifier.send_flow_run_finished_slack_notification],
on_crashed=[slack_notifier.send_flow_run_finished_slack_notification],
log_prints=True,
)
async def internal_alerting():
...
- Batch your submissions to avoid a stampede:
active = [cfg for cfg in configs if cfg.is_active()]
batch_size = 10
tasks = []
for i in range(0, len(active), batch_size):
batch = active[i : i + batch_size]
batch_tasks = [alert_service.process_alert.submit(cfg) for cfg in batch]
# Option A: wait per-batch (smooths load)
resolve_futures_to_states(batch_tasks)
tasks.extend(batch_tasks)
# Or, if you’d rather wait once at the end:
# resolve_futures_to_states(tasks)
- If you keep the async flow, a tiny yield between submissions can help smooth scheduling:
import asyncio
tasks = []
for cfg in configs:
if not cfg.is_active():
continue
tasks.append(alert_service.process_alert.submit(cfg))
await asyncio.sleep(0) # yield to event loop to avoid a submission burst
- Alternative to resolve_futures_to_states
- It’s fine to keep using it, but if you want to test whether a different wait path avoids the crash, you can do:
for f in tasks:
f.wait()
task_end_states = [f.state for f in tasks]
- Functionally equivalent for your “did anything fail?” check, but takes a slightly different path through the engine.
A couple of questions to help us pinpoint and give you a more tailored workaround:
- Which Prefect version and Python version are you on?
- Roughly how many tasks are you submitting per run (number of active configs)?
- What task runner/worker type and environment (e.g., Kubernetes, Docker, local) are you using?
If you’re okay with it, I can add your traceback and pattern (async flow + many concurrent `.submit`s) to the GitHub issue above to help the team prioritize.