{"record":{"id":"a786915a11f30a30","repo":"microsoft/autogen","slug":"the-recent-update-is-not-a-toolcallmessage","errorCode":null,"errorMessage":"The recent update is not a ToolCallMessage","messagePattern":"The recent update is not a ToolCallMessage","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"dotnet/src/AutoGen.Core/Middleware/PrintMessageMiddleware.cs","lineNumber":96,"sourceCode":"                }\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                {\n                    throw new InvalidOperationException(\"The recent update is not a ToolCallMessage\");\n                }\n            }\n            else if (message is IMessage imessage)\n            {\n                recentUpdate = imessage;\n\n                yield return imessage;\n            }\n            else\n            {\n                throw new InvalidOperationException(\"The message is not a valid message\");\n            }\n        }\n        Console.WriteLine();\n        if (recentUpdate is not null && recentUpdate is not TextMessage)\n        {\n            Console.WriteLine(recentUpdate.FormatMessage());\n        }","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/dotnet/src/AutoGen.Core/Middleware/PrintMessageMiddleware.cs#L78-L114","documentation":"Thrown by PrintMessageMiddleware while folding a streaming reply. A ToolCallMessageUpdate arrived but the message accumulated so far (recentUpdate) is not null and is not a ToolCallMessage (typically a TextMessage), so the tool-call delta cannot be merged and an InvalidOperationException is thrown.","triggerScenarios":"An IStreamingAgent's GenerateStreamingReplyAsync yields a TextMessageUpdate (creating a TextMessage accumulator) and later yields a ToolCallMessageUpdate in the same stream; the else branch at PrintMessageMiddleware.cs:96 fires.","commonSituations":"Function-calling models that stream some text before emitting tool-call arguments, custom agents mixing text deltas and tool-call deltas, or middleware that converts early chunks into complete messages.","solutions":["Make the streaming agent emit tool-call updates without preceding text updates in the same stream (or emit them as separate replies)","If text-then-tool-call is required by your model flow, bypass PrintMessageMiddleware for that agent","Reorder middleware so a connector normalizes the stream (e.g. GeminiMessageConnector) before PrintMessageMiddleware sees it"],"exampleFix":"// before: yield return new TextMessageUpdate(...); ... yield return new ToolCallMessageUpdate(...);\n// after: keep one update type per stream\nif (hasToolCalls) { yield return new ToolCallMessageUpdate(...); } // no text updates before it","handlingStrategy":"validation","validationCode":"// Ensure tool-call streams don't follow text updates from the same agent\nbool sawTextUpdate = false;\nawait foreach (var m in agent.GenerateStreamingReplyAsync(msgs, ct))\n{\n    if (m is TextMessageUpdate) sawTextUpdate = true;\n    if (sawTextUpdate && m is ToolCallMessageUpdate)\n        throw new InvalidOperationException(\"Tool-call update after text update in same stream\");\n}","typeGuard":"static bool IsToolCallUpdateStream(IEnumerable<IMessage> replayed) =>\n    replayed.All(m => m is ToolCallMessageUpdate or ToolCallMessage);","tryCatchPattern":"try { await EnumerateStreamAsync(printingAgent, msgs, ct); }\ncatch (InvalidOperationException e) when (e.Message.Contains(\"not a ToolCallMessage\"))\n{ /* skip printing for this agent or split the stream */ }","preventionTips":["Emit tool-call deltas in a dedicated stream without text preamble","Normalize streams with a provider connector middleware before printing middleware"],"tags":["autogen","streaming","middleware","tool-call"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}