fix: resolve first-time trace prompt not appearing for Flows and standalone Crews

Two places had the same race condition between FlowStartedEvent /
  CrewKickoffStartedEvent handlers and DefaultEnvEvent in the thread pool.

  Commit 929d756ae introduced env-detection events (DefaultEnvEvent etc.)
  dispatched through _handle_action_event, which has a fallback that calls
  initialize_batch() without claiming batch ownership. get_env_context() is
  called at the very top of both Flow.kickoff() and Crew.kickoff(), so
  DefaultEnvEvent can fire and win the thread pool race before the context
  event handler runs. When that happened, is_batch_initialized() returned
  True and _initialize_flow_batch / _initialize_crew_batch were skipped,
  leaving batch_owner_type=None. The completion checks
  (batch_owner_type == "flow" / "crew") then failed silently and the
  first-time trace prompt never appeared.

  Fix: remove the is_batch_initialized() guard from on_flow_started and
  replace it with an unconditional call to _initialize_flow_batch.
  initialize_batch() is already idempotent (lock-guarded early exit), so
  batch_owner_type="flow" is set regardless of which event initialized the
  batch first.

  For on_crew_started, apply the same pattern but guard against overriding
  a parent flow's ownership: call _initialize_crew_batch unconditionally
  unless batch_owner_type is already "flow".

  Also suppress the "Tracing is disabled" panel in ConsoleFormatter when
  the first-time handler is active, preventing a confusing mid-flow message
  before the trace prompt appears at the end.
This commit is contained in:
Tiago Freire
2026-03-23 23:51:45 -03:00
parent 8df07cddf8
commit 46bb7aa79e
2 changed files with 19 additions and 3 deletions

View File

@@ -235,8 +235,11 @@ class TraceCollectionListener(BaseEventListener):
@event_bus.on(FlowStartedEvent)
def on_flow_started(source: Any, event: FlowStartedEvent) -> None:
if not self.batch_manager.is_batch_initialized():
self._initialize_flow_batch(source, event)
# Always call _initialize_flow_batch to claim ownership.
# If batch was already initialized by a concurrent action event
# (race condition), initialize_batch() returns early but
# batch_owner_type is still correctly set to "flow".
self._initialize_flow_batch(source, event)
self._handle_trace_event("flow_started", source, event)
@event_bus.on(MethodExecutionStartedEvent)
@@ -266,7 +269,12 @@ class TraceCollectionListener(BaseEventListener):
@event_bus.on(CrewKickoffStartedEvent)
def on_crew_started(source: Any, event: CrewKickoffStartedEvent) -> None:
if not self.batch_manager.is_batch_initialized():
if self.batch_manager.batch_owner_type != "flow":
# Always call _initialize_crew_batch to claim ownership.
# If batch was already initialized by a concurrent action event
# (race condition with DefaultEnvEvent), initialize_batch() returns
# early but batch_owner_type is still correctly set to "crew".
# Skip only when a parent flow already owns the batch.
self._initialize_crew_batch(source, event)
self._handle_trace_event("crew_kickoff_started", source, event)

View File

@@ -127,6 +127,9 @@ To update, run: uv sync --upgrade-package crewai"""
def _show_tracing_disabled_message_if_needed(self) -> None:
"""Show tracing disabled message if tracing is not enabled."""
from crewai.events.listeners.tracing.trace_listener import (
TraceCollectionListener,
)
from crewai.events.listeners.tracing.utils import (
has_user_declined_tracing,
is_tracing_enabled_in_context,
@@ -136,6 +139,11 @@ To update, run: uv sync --upgrade-package crewai"""
if should_suppress_tracing_messages():
return
# Don't show "disabled" message when the first-time handler will show
# the trace prompt after execution completes (avoids confusing mid-flow messages)
if TraceCollectionListener._instance and TraceCollectionListener._instance.first_time_handler.is_first_time:
return
if not is_tracing_enabled_in_context():
if has_user_declined_tracing():
message = """Info: Tracing is disabled.