diff --git a/python/packages/devui/agent_framework_devui/_mapper.py b/python/packages/devui/agent_framework_devui/_mapper.py index f68fea9d01..5adff1cd2f 100644 --- a/python/packages/devui/agent_framework_devui/_mapper.py +++ b/python/packages/devui/agent_framework_devui/_mapper.py @@ -840,7 +840,9 @@ async def _convert_workflow_event(self, event: Any, context: dict[str, Any]) -> if event_class == "WorkflowFailedEvent": workflow_id = context.get("workflow_id", str(uuid4())) - error_info = getattr(event, "error", None) + # WorkflowFailedEvent uses 'details' field (WorkflowErrorDetails), not 'error' + # This matches ExecutorFailedEvent which also uses 'details' + details = getattr(event, "details", None) # Import Response and ResponseError types from openai.types.responses import Response, ResponseError @@ -849,8 +851,14 @@ async def _convert_workflow_event(self, event: Any, context: dict[str, Any]) -> request_obj = context.get("request") model_name = request_obj.model if request_obj and request_obj.model else "devui" - # Create error object - error_message = str(error_info) if error_info else "Unknown error" + # Extract error message from WorkflowErrorDetails + if details: + error_message = getattr(details, "message", None) or str(details) + extra = getattr(details, "extra", None) + if extra: + error_message = f"{error_message} (extra: {extra})" + else: + error_message = "Unknown error" # Create ResponseError object (code must be one of the allowed values) response_error = ResponseError( @@ -945,7 +953,13 @@ async def _convert_workflow_event(self, event: Any, context: dict[str, Any]) -> item_id = context.get(f"exec_item_{executor_id}", f"exec_{executor_id}_unknown") # ExecutorFailedEvent uses 'details' field (WorkflowErrorDetails), not 'error' details = getattr(event, "details", None) - err_msg: str | None = str(getattr(details, "message", details)) if details else None + if details: + err_msg = getattr(details, "message", None) or str(details) + extra = getattr(details, "extra", None) + if extra: + err_msg = f"{err_msg} (extra: {extra})" + else: + err_msg = None # Create ExecutorActionItem with failed status executor_item = ExecutorActionItem( diff --git a/python/packages/devui/tests/test_mapper.py b/python/packages/devui/tests/test_mapper.py index eaaec77313..6557dea527 100644 --- a/python/packages/devui/tests/test_mapper.py +++ b/python/packages/devui/tests/test_mapper.py @@ -596,6 +596,31 @@ async def test_workflow_failed_event(mapper: MessageMapper, test_request: AgentF response = failed_events[0].response assert response.status == "failed" assert response.error is not None + # Verify error message is correctly extracted from details.message (not "Unknown error") + assert "Workflow failed due to test error" in response.error.message + assert "Unknown error" not in response.error.message + + +async def test_workflow_failed_event_with_extra(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None: + """Test WorkflowFailedEvent includes extra context when available.""" + from agent_framework._workflows._events import WorkflowErrorDetails, WorkflowFailedEvent + + details = WorkflowErrorDetails( + error_type="ValidationError", + message="Input validation failed", + executor_id="validation_executor", + extra={"field": "email", "reason": "invalid format"}, + ) + event = WorkflowFailedEvent(details=details) + events = await mapper.convert_event(event, test_request) + + assert len(events) == 1 + assert events[0].type == "response.failed" + response = events[0].response + # Verify both the message and extra context are included + assert "Input validation failed" in response.error.message + assert "extra:" in response.error.message + assert "email" in response.error.message async def test_workflow_failed_event_with_traceback(mapper: MessageMapper, test_request: AgentFrameworkRequest) -> None: