diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/AgentResponseUpdateExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/AgentResponseUpdateExtensions.cs
index 7930cba8380..f6c0cb1ac19 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/AgentResponseUpdateExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/AgentResponseUpdateExtensions.cs
@@ -305,7 +305,14 @@ private static StreamingWorkflowEventComplete CreateWorkflowEventResponse(Workfl
if (JsonSerializer.IsReflectionEnabledByDefault)
{
JsonElement? dataElement = null;
- if (workflowEvent.Data is not null)
+ if (workflowEvent.Data is Exception exception)
+ {
+ // Exceptions cannot go through System.Text.Json: Exception.TargetSite is a MethodBase,
+ // which throws NotSupportedException and would surface as a serialization error instead
+ // of the actual failure. Report the message only; the stack trace is not for clients.
+ dataElement = JsonSerializer.SerializeToElement(exception.Message, OpenAIHostingJsonContext.Default.String);
+ }
+ else if (workflowEvent.Data is not null)
{
dataElement = JsonSerializer.SerializeToElement(workflowEvent.Data, OpenAIHostingJsonUtilities.DefaultOptions.GetTypeInfo(typeof(object)));
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/WorkflowErrorEventStreamingTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/WorkflowErrorEventStreamingTests.cs
new file mode 100644
index 00000000000..6ac22ecf3a8
--- /dev/null
+++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/WorkflowErrorEventStreamingTests.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.Agents.AI.Hosting.OpenAI.Responses;
+using Microsoft.Agents.AI.Hosting.OpenAI.Responses.Models;
+using Microsoft.Agents.AI.Workflows;
+using Microsoft.Extensions.AI;
+
+namespace Microsoft.Agents.AI.Hosting.OpenAI.UnitTests;
+
+///
+/// Regression tests for a failing workflow used as an agent: the emitted update carries a
+/// as its raw representation, whose data is the original
+/// . Streaming that update must not try to JSON-serialize the exception,
+/// which fails on Exception.TargetSite and hides the actual workflow failure.
+///
+public sealed class WorkflowErrorEventStreamingTests
+{
+ [Fact]
+ public async Task WorkflowErrorEvent_IsStreamedWithoutSerializationFailureAsync()
+ {
+ // Arrange: the update a failing workflow-as-agent produces.
+ const string ErrorMessage = "Executor 'AggregatorExecutor' cannot send messages of type 'RawAggregate'.";
+ var update = new AgentResponseUpdate(ChatRole.Assistant, [new ErrorContent(ErrorMessage)])
+ {
+ RawRepresentation = new WorkflowErrorEvent(Thrown(ErrorMessage))
+ };
+
+ var request = new CreateResponse { Input = "Hello", Stream = true };
+ var context = new AgentInvocationContext(new IdGenerator("resp_1", "conv_1"));
+
+ // Act
+ List events = [];
+ await foreach (var evt in ToAsyncEnumerableAsync(update).ToStreamingResponseAsync(request, context))
+ {
+ events.Add(evt);
+ }
+
+ // Assert: the workflow event is streamed and carries the real failure message.
+ var workflowEvent = Assert.Single(events.OfType());
+ Assert.NotNull(workflowEvent.Data);
+ JsonElement data = workflowEvent.Data.Value;
+ Assert.Equal(nameof(WorkflowErrorEvent), data.GetProperty("event_type").GetString());
+ Assert.Equal(ErrorMessage, data.GetProperty("data").GetString());
+ }
+
+ /// Returns an actually-thrown exception, so TargetSite is populated.
+ private static InvalidOperationException Thrown(string message)
+ {
+ try
+ {
+ throw new InvalidOperationException(message);
+ }
+ catch (InvalidOperationException ex)
+ {
+ return ex;
+ }
+ }
+
+ private static async IAsyncEnumerable ToAsyncEnumerableAsync(params AgentResponseUpdate[] updates)
+ {
+ foreach (var update in updates)
+ {
+ yield return update;
+ }
+
+ await Task.CompletedTask;
+ }
+}