Problem
When an agent uses tools mid-response (text → tool calls → more text), the Teams streaming protocol (TeamsHttpStream using streaminfo entities) causes messages to be silently lost or partially delivered.
Two failure modes observed:
-
403 ContentStreamNotAllowed: Teams rejects streaming chunks with "Request streamed content should contain the previously streamed content". This happens when the accumulated text changes shape between text segments (before vs after tool calls).
-
Silent loss: The stream POST returns 200 but Teams doesn't render the content (likely because the stream timed out during tool execution). No error is raised, so the fallback delivery path (preparePayload → deliver()) is never triggered. The message simply vanishes.
Root cause
preparePayload() in reply-stream-controller.ts checks stream.hasContent to decide whether to suppress the fallback deliver() path. When the stream partially succeeds (first text segment streams fine), hasContent returns true and the fallback is suppressed. But subsequent text segments after tool calls either get 403'd or silently dropped by Teams.
The stream controller doesn't account for multi-segment responses interrupted by tool calls. It assumes continuous text generation.
Reproduction
- Configure an msteams bot with a tool-using agent (e.g., one that calls
exec or read)
- Send a message that triggers tool use mid-response
- Observe: first text segment may stream, second segment after tools is lost
Workaround
Set stream = undefined in reply-stream-controller.ts to disable streaming entirely. Messages then deliver via the standard proactive messaging path.
Environment
- OpenClaw v2026.3.24
- Microsoft Teams (personal DM scope)
- Agent with exec/read tools enabled
Problem
When an agent uses tools mid-response (text → tool calls → more text), the Teams streaming protocol (
TeamsHttpStreamusingstreaminfoentities) causes messages to be silently lost or partially delivered.Two failure modes observed:
403
ContentStreamNotAllowed: Teams rejects streaming chunks with"Request streamed content should contain the previously streamed content". This happens when the accumulated text changes shape between text segments (before vs after tool calls).Silent loss: The stream POST returns 200 but Teams doesn't render the content (likely because the stream timed out during tool execution). No error is raised, so the fallback delivery path (
preparePayload→deliver()) is never triggered. The message simply vanishes.Root cause
preparePayload()inreply-stream-controller.tschecksstream.hasContentto decide whether to suppress the fallbackdeliver()path. When the stream partially succeeds (first text segment streams fine),hasContentreturns true and the fallback is suppressed. But subsequent text segments after tool calls either get 403'd or silently dropped by Teams.The stream controller doesn't account for multi-segment responses interrupted by tool calls. It assumes continuous text generation.
Reproduction
execorread)Workaround
Set
stream = undefinedinreply-stream-controller.tsto disable streaming entirely. Messages then deliver via the standard proactive messaging path.Environment