{"record":{"id":"9f24875cca060592","repo":"microsoft/autogen","slug":"the-stream-should-have-returned-the-final-result","errorCode":null,"errorMessage":"The stream should have returned the final result.","messagePattern":"The stream should have returned the final result\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/Microsoft.AutoGen/AgentChat/Abstractions/Tasks.cs","lineNumber":86,"sourceCode":"    /// <remarks>\n    /// The runner is stateful and a subsequent call to this method will continue from where the previous\n    /// call left off.If the task is not specified,the runner will continue with the current task.\n    /// </remarks>\n    /// <param name=\"task\">The task definition as a message.</param>\n    /// <param name=\"cancellationToken\"></param>\n    /// <returns>The result of running the task.</returns>\n    /// <exception cref=\"InvalidOperationException\">If no response is generated.</exception>\n    public async ValueTask<TaskResult> RunAsync(ChatMessage task, CancellationToken cancellationToken = default)\n    {\n        await foreach (TaskFrame frame in this.StreamAsync(task, cancellationToken))\n        {\n            if (frame.Type == TaskFrame.FrameType.Response)\n            {\n                return frame.Response!;\n            }\n        }\n\n        throw new InvalidOperationException(\"The stream should have returned the final result.\");\n    }\n\n    /// <summary>\n    /// Run the task and produce a stream of <see cref=\"TaskFrame\"/> and the final <see cref=\"TaskResult\"/>\n    /// is the last frame in the stream.\n    /// </summary>\n    /// <remarks>\n    /// The runner is stateful and a subsequent call to this method will continue from where the previous\n    /// call left off.If the task is not specified,the runner will continue with the current task.\n    /// </remarks>\n    /// <param name=\"task\">The task definition as a string.</param>\n    /// <param name=\"cancellationToken\"></param>\n    /// <returns>A stream of <see cref=\"TaskFrame\"/> containing internal messages and intermediate results followed by\n    /// the final <see cref=\"TaskResult\"/></returns>\n    public IAsyncEnumerable<TaskFrame> StreamAsync(string task, CancellationToken cancellationToken = default) =>\n        this.StreamAsync(ToMessage(task), cancellationToken);\n\n    /// <summary>","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/dotnet/src/Microsoft.AutoGen/AgentChat/Abstractions/Tasks.cs#L68-L104","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (inside StreamAsync)\nforeach (var f in inner) { yield return f; } // may end without Response\n// after\nTaskResult? final = null;\nawait foreach (var f in inner) { if (f.Type == TaskFrame.FrameType.Response) final = f.Response; yield return f; }\nif (final is null) yield return new TaskFrame(TaskFrame.FrameType.Response, response: SynthesizeResult());","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { var result = await task.RunAsync(msg, ct); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"final result\"))\n{\n    // fall back to consuming the stream yourself: await foreach (var frame in task.StreamAsync(msg, ct)) { ... } \n}","preventionTips":["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"],"tags":["autogen","task-runner","streaming","contract","csharp"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}