microsoft/autogen · error · InvalidOperationException
The stream should have returned the final result.
Error message
The stream should have returned the final result.
What it means
Task.RunAsync consumes StreamAsync and returns the first TaskFrame of FrameType.Response. If the stream completes without ever yielding a Response frame (every frame was text/output or the stream ended early), RunAsync throws InvalidOperationException — the task runner's contract is that the stream must end with a final result frame.
Source
Thrown at dotnet/src/Microsoft.AutoGen/AgentChat/Abstractions/Tasks.cs:86
/// <remarks>
/// The runner is stateful and a subsequent call to this method will continue from where the previous
/// call left off.If the task is not specified,the runner will continue with the current task.
/// </remarks>
/// <param name="task">The task definition as a message.</param>
/// <param name="cancellationToken"></param>
/// <returns>The result of running the task.</returns>
/// <exception cref="InvalidOperationException">If no response is generated.</exception>
public async ValueTask<TaskResult> RunAsync(ChatMessage task, CancellationToken cancellationToken = default)
{
await foreach (TaskFrame frame in this.StreamAsync(task, cancellationToken))
{
if (frame.Type == TaskFrame.FrameType.Response)
{
return frame.Response!;
}
}
throw new InvalidOperationException("The stream should have returned the final result.");
}
/// <summary>
/// Run the task and produce a stream of <see cref="TaskFrame"/> and the final <see cref="TaskResult"/>
/// is the last frame in the stream.
/// </summary>
/// <remarks>
/// The runner is stateful and a subsequent call to this method will continue from where the previous
/// call left off.If the task is not specified,the runner will continue with the current task.
/// </remarks>
/// <param name="task">The task definition as a string.</param>
/// <param name="cancellationToken"></param>
/// <returns>A stream of <see cref="TaskFrame"/> containing internal messages and intermediate results followed by
/// the final <see cref="TaskResult"/></returns>
public IAsyncEnumerable<TaskFrame> StreamAsync(string task, CancellationToken cancellationToken = default) =>
this.StreamAsync(ToMessage(task), cancellationToken);
/// <summary>View on GitHub (pinned to 027ecf0a37)
Solutions
- Ensure the StreamAsync implementation always emits a TaskFrame(FrameType.Response, ...) before completing, even on early termination
- Prefer calling StreamAsync directly and handling frames yourself if the runner may legitimately end without a final response
- Audit middleware/agents in the pipeline for frames being filtered or the loop exiting before the response is produced
Example fix
// before (inside StreamAsync)
foreach (var f in inner) { yield return f; } // may end without Response
// after
TaskResult? final = null;
await foreach (var f in inner) { if (f.Type == TaskFrame.FrameType.Response) final = f.Response; yield return f; }
if (final is null) yield return new TaskFrame(TaskFrame.FrameType.Response, response: SynthesizeResult()); Defensive patterns
Strategy: try-catch
Try / catch
try { var result = await task.RunAsync(msg, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("final result"))
{
// fall back to consuming the stream yourself: await foreach (var frame in task.StreamAsync(msg, ct)) { ... }
} Prevention
- If you implement task runners, guarantee StreamAsync always ends with a Response frame
- Add a contract unit test: every early-exit path still emits the final frame
- Handle StreamAsync directly when non-final termination is legitimate for your workflow
When it happens
Trigger: A custom or misconfigured task runner whose StreamAsync terminates without emitting a Response frame (e.g. an agent loop that stops on a limit without producing a final result); cancellation/error paths that close the stream instead of propagating the exception; a middleware chain that drops the final frame.
Common situations: Writing custom ITask implementations; combining agents/middleware that swallow the response frame; early-termination conditions (max-turn limits, stop words) that end the loop without a synthesized final response.
Related errors
- The agent did not produce a final response. Check the agent'
- The recent update is not a TextMessage
- Only one choice is supported in streaming response
- Please set OPENAI_API_KEY environment variable.
- Failed to deserialize response
AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15).
Data as JSON: /api/errors/9f24875cca060592.
Report an issue: GitHub.