You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
StreamingResponse: mutating the finalized "final" stream activity (delete or update) after end_stream() re-hangs the Teams client's typing/streaming indicator #535
microsoft-agents-hosting-core 1.3.0 (also confirmed present on main/1.4.0-dev — the relevant streaming_response.py code is byte-identical between our installed 1.3.0 and the current main branch, so this is not fixed in the unreleased version either)
Python 3.12, Teams personal chat channel
Summary
When a StreamingResponse finishes via end_stream(), it sends a final Activity(type="message", entities=[StreamInfo(stream_type="final", ...)]), whose activity.id is the stream's _stream_id (assigned from the first typing activity's response.id). If that same activity is later mutated — via either TurnContext.delete_activity(activity_id) or TurnContext.update_activity(activity) — the Teams client's streaming/typing indicator hangs indefinitely, even though a real message has already been delivered to the user. This happens even when the mutation occurs well after the stream has ended, from a separate proactive continuation.
Repro scenario
Handle a Teams message with a streamed response: queue_informative_update() → queue_text_chunk() × N → end_stream(). Capture the resulting activity id (streamer._stream_id, since the public stream_id property was removed).
In a scenario where the full answer isn't ready within Teams' streaming window, deliver the rest of the answer later via a separate proactive turn (adapter.continue_conversation(...)), and either:
a) await context.delete_activity(saved_stream_activity_id), or
b) await context.update_activity(new_activity_with_id=saved_stream_activity_id)
...to clean up or replace the "please wait" placeholder text.
Observe the Teams client: the typing/streaming indicator remains stuck/hanging, even though the conversation already shows a completed message.
Expected
Mutating a previously finalized stream activity via the standard TurnContext.delete_activity/update_activity APIs shouldn't affect client-side streaming state after the stream has already ended (end_stream() already sent the stream_type="final" marker).
Actual
The Teams client appears to tie its streaming-in-progress UI state to that specific activity's content/existence persisting unchanged. Any later mutation — delete or edit — re-triggers/orphans that state, and the indicator never clears.
Workaround in use
Leave the finalized stream activity completely untouched after end_stream(). Confirmed via testing that both "delete" and "edit" trigger the hang, while "leave untouched" does not.
What I checked before filing changelog.md (v1.4.0 Unreleased section), Context7 docs for /microsoft/agents-for-python, and GitHub issue/PR search for StreamingResponse, typing indicator, end_stream, delete_activity — found #397/#398 (a different, already-fixed issue about the generic per-turn TypingIndicator/TypingOptions mechanism, unrelated to StreamingResponse), but nothing addressing this specific scenario.
Hi AHany-Henkel, thanks for posting this question. One quick question before I start to investigate this: when you say the Teams client, do you mean the TeamsConnectorClient from microsoft-agents-hosting-core?
Hi Rodrigo Brandão (@rodrigobr-msft), thanks for jumping on this! To clarify — by "the Teams client" I mean the actual Teams app itself (desktop/web), specifically that animated typing/streaming indicator you see in the chat while a message is coming in. Not TeamsConnectorClient.
The piece that's actually involved is StreamingResponse (microsoft_agents.hosting.core.app.streaming.streaming_response.StreamingResponse). When end_stream() runs, it sends a final Activity(type="message", entities=[StreamInfo(stream_type="final", ...)]) through _queue_next_chunk(). That final activity reuses self._stream_id — which is actually set from the response of the first activity in the stream (the first typing update), not the final one, and just carried forward from there.
Here's the actual bug: once that final activity has gone out and the stream's done, if you later call TurnContext.delete_activity() or TurnContext.update_activity() on that same activity id (in a separate, later turn), the Teams UI's typing indicator hangs for about 5 seconds before it clears. It's not stuck forever, but it's a clearly avoidable delay since the real message already made it to the user by then.
TeamsConnectorClient is just the transport doing the actual delete/update call under the hood — create_connector_client() always builds one of those regardless of channel, and that's what update_activity/delete_activity call into. The real issue is how the Teams client-side UI reacts when a StreamingResponse-finalized activity gets touched again after the fact.
Hi AHany-Henkel, Rodrigo Brandão (@rodrigobr-msft). I was able to reproduce the issue and it's due to the default typing indicator triggered by AgentApplication. update_activity and delete_activity don't pass through the “message sent” hook that stops the timer, so Teams lets that typing activity expire naturally (about five seconds).
I will try registering on_update_activity and on_delete_activity hooks in TypingIndicator so a mutation can stop the timer.
Activity
rodrigobr-msft commentedon Aug 11, 2026
Hi AHany-Henkel, thanks for posting this question. One quick question before I start to investigate this: when you say the Teams client, do you mean the
TeamsConnectorClientfrommicrosoft-agents-hosting-core?AHany-Henkel commentedon Aug 11, 2026
Hi Rodrigo Brandão (@rodrigobr-msft), thanks for jumping on this! To clarify — by "the Teams client" I mean the actual Teams app itself (desktop/web), specifically that animated typing/streaming indicator you see in the chat while a message is coming in. Not
TeamsConnectorClient.The piece that's actually involved is
StreamingResponse(microsoft_agents.hosting.core.app.streaming.streaming_response.StreamingResponse). Whenend_stream()runs, it sends a finalActivity(type="message", entities=[StreamInfo(stream_type="final", ...)])through_queue_next_chunk(). That final activity reusesself._stream_id— which is actually set from the response of the first activity in the stream (the first typing update), not the final one, and just carried forward from there.Here's the actual bug: once that final activity has gone out and the stream's done, if you later call
TurnContext.delete_activity()orTurnContext.update_activity()on that same activity id (in a separate, later turn), the Teams UI's typing indicator hangs for about 5 seconds before it clears. It's not stuck forever, but it's a clearly avoidable delay since the real message already made it to the user by then.TeamsConnectorClientis just the transport doing the actual delete/update call under the hood —create_connector_client()always builds one of those regardless of channel, and that's whatupdate_activity/delete_activitycall into. The real issue is how the Teams client-side UI reacts when aStreamingResponse-finalized activity gets touched again after the fact.ceciliaavila commentedon Sep 1, 2026
Hi AHany-Henkel, Rodrigo Brandão (@rodrigobr-msft). I was able to reproduce the issue and it's due to the default typing indicator triggered by AgentApplication.
update_activityanddelete_activitydon't pass through the “message sent” hook that stops the timer, so Teams lets that typing activity expire naturally (about five seconds).I will try registering on_update_activity and on_delete_activity hooks in TypingIndicator so a mutation can stop the timer.
[#535] StreamingResponse: mutating the finalized "final" stream activ…