{"record":{"id":"be6d7473bbcfeb3e","repo":"microsoft/autogen","slug":"the-recent-update-is-not-a-textmessage","errorCode":null,"errorMessage":"The recent update is not a TextMessage","messagePattern":"The recent update is not a TextMessage","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/AutoGen.Core/Middleware/PrintMessageMiddleware.cs","lineNumber":77,"sourceCode":"                {\n                    // Print from: xxx\n                    Console.WriteLine($\"from: {textMessageUpdate.From}\");\n                    recentUpdate = new TextMessage(textMessageUpdate);\n                    Console.Write(textMessageUpdate.Content);\n\n                    yield return message;\n                }\n                else if (recentUpdate is TextMessage recentTextMessage)\n                {\n                    // Print the content of the message\n                    Console.Write(textMessageUpdate.Content);\n                    recentTextMessage.Update(textMessageUpdate);\n\n                    yield return recentTextMessage;\n                }\n                else\n                {\n                    throw new InvalidOperationException(\"The recent update is not a TextMessage\");\n                }\n            }\n            else if (message is ToolCallMessageUpdate toolCallUpdate)\n            {\n                if (recentUpdate is null)\n                {\n                    recentUpdate = new ToolCallMessage(toolCallUpdate);\n\n                    yield return message;\n                }\n                else if (recentUpdate is ToolCallMessage recentToolCallMessage)\n                {\n                    recentToolCallMessage.Update(toolCallUpdate);\n\n                    yield return message;\n                }\n                else\n                {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/dotnet/src/AutoGen.Core/Middleware/PrintMessageMiddleware.cs#L59-L95","documentation":"Thrown by PrintMessageMiddleware while relaying a streaming reply. The middleware folds streaming chunks into one accumulated message; when a TextMessageUpdate arrives and the message accumulated so far (recentUpdate) is not null and not a TextMessage, it cannot append text to it (e.g. a ToolCallMessage was accumulated first) and throws InvalidOperationException.","triggerScenarios":"An IStreamingAgent's GenerateStreamingReplyAsync yields a ToolCallMessageUpdate (or any non-text IMessage) first, then yields a TextMessageUpdate afterwards; the middleware's else branch at PrintMessageMiddleware.cs:77 fires because recentUpdate is a ToolCallMessage, not a TextMessage.","commonSituations":"Agents that emit tool-call chunks followed by text chunks in the same stream, custom agents mixing update types in one streaming response, or middleware pipelines that inject intermediate non-update messages before text deltas.","solutions":["Fix the streaming agent so one stream contains only one kind of update (all TextMessageUpdate or all ToolCallMessageUpdate), not interleaved types","If you control the agent, start the stream with the text updates before emitting any complete non-text IMessage","Remove or reorder PrintMessageMiddleware in the agent's middleware chain if you must interleave heterogeneous messages","Catch InvalidOperationException around the streaming enumeration only as a last resort, since the console printout will already be partially written"],"exampleFix":"// before: agent yields ToolCallMessageUpdate, then TextMessageUpdate in the same stream\n// after: yield text updates first, or split into separate replies\nawait foreach (var update in textUpdates) yield return update; // only TextMessageUpdate in this stream","handlingStrategy":"validation","validationCode":"// Before consuming a stream through PrintMessageMiddleware, verify homogeneous update types\nvar types = new HashSet<Type>();\nawait foreach (var m in agent.GenerateStreamingReplyAsync(msgs, ct)) types.Add(m.GetType());\nif (types.Count > 1 && types.Contains(typeof(TextMessageUpdate)))\n    throw new InvalidOperationException(\"Stream mixes text updates with other message types\");","typeGuard":"static bool IsTextUpdateStream(IEnumerable<IMessage> replayed) =>\n    replayed.All(m => m is TextMessageUpdate or TextMessage);","tryCatchPattern":"try { await foreach (var m in printingAgent.GenerateStreamingReplyAsync(msgs, ct)) { /* ... */ } }\ncatch (InvalidOperationException e) when (e.Message.Contains(\"not a TextMessage\"))\n{ /* fall back to non-streaming call */ }","preventionTips":["Keep one update type per streaming reply in custom IStreamingAgent implementations","Test custom streaming agents with PrintMessageMiddleware enabled"],"tags":["autogen","streaming","middleware","csharp"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}