{"record":{"id":"817d4f630134cb82","repo":"microsoft/autogen","slug":"role-mismatch","errorCode":null,"errorMessage":"Role mismatch","messagePattern":"Role mismatch","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"dotnet/src/AutoGen.Core/Message/TextMessage.cs","lineNumber":26,"sourceCode":"    public TextMessage(Role role, string content, string? from = null)\n    {\n        this.Content = content;\n        this.Role = role;\n        this.From = from;\n    }\n\n    public TextMessage(TextMessageUpdate update)\n    {\n        this.Content = update.Content ?? string.Empty;\n        this.Role = update.Role;\n        this.From = update.From;\n    }\n\n    public void Update(TextMessageUpdate update)\n    {\n        if (update.Role != this.Role)\n        {\n            throw new System.ArgumentException(\"Role mismatch\", nameof(update));\n        }\n\n        if (update.From != this.From)\n        {\n            throw new System.ArgumentException(\"From mismatch\", nameof(update));\n        }\n\n        this.Content = this.Content + update.Content ?? string.Empty;\n    }\n\n    public Role Role { get; set; }\n\n    public string Content { get; set; }\n\n    public string? From { get; set; }\n\n    public override string ToString()\n    {","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/dotnet/src/AutoGen.Core/Message/TextMessage.cs#L8-L44","documentation":"TextMessage.Update(TextMessageUpdate) is used to append streaming deltas onto an existing message, and requires update.Role to equal the message's current Role; otherwise it throws ArgumentException('Role mismatch', paramName: update). This guards incremental streaming state from being corrupted by a chunk that belongs to a different role (e.g. system vs assistant).","triggerScenarios":"Streaming updates where the first chunk is Role.Assistant and a later chunk is Role.System (or Function); constructing TextMessageUpdate with a default/unset Role; merging chunks from two different streams into one message.","commonSituations":"Custom streaming pipelines that forward every delta regardless of role; deserializing update events where the role field is missing and defaults differently; mixing system-prompt injection events into the assistant token stream.","solutions":["Only call Update with chunks whose Role matches the message created from the first chunk.","Start a new TextMessage when the role changes instead of updating the existing one.","Set Role explicitly when constructing each TextMessageUpdate rather than relying on defaults.","When merging streams, key messages by (From, Role) so chunks route to the right accumulator."],"exampleFix":"// before\nvar msg = new TextMessage(update1); // Role.Assistant\nmsg.Update(update2); // update2.Role == Role.System -> throws\n\n// after\nif (update2.Role != msg.Role)\n{\n    msg = new TextMessage(update2); // start a new message for the new role\n}\nelse\n{\n    msg.Update(update2);\n}","handlingStrategy":"validation","validationCode":"if (update.Role != msg.Role)\n    msg = new TextMessage(update); // start a new message instead of updating\nelse\n    msg.Update(update);","typeGuard":"static bool IsSameRole(TextMessage msg, TextMessageUpdate update) => update.Role == msg.Role;","tryCatchPattern":"try { msg.Update(update); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"Role mismatch\"))\n{\n    msg = new TextMessage(update); // role changed: begin a new message\n}","preventionTips":["Key streaming accumulators by (From, Role) so chunks never cross roles.","Always set Role explicitly on TextMessageUpdate instances.","Treat a role change mid-stream as a new message, never an update."],"tags":["autogen","dotnet","message","streaming","text-message","validation","argumentexception"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}