Bug type
Behavior bug (incorrect output/state without crash)
Summary
When users post in different threads within the same Teams channel, the bot mixes conversation context between threads. A question in Thread A includes context from Thread B. Each thread should have its own isolated session.
Version
- OpenClaw: 2026.3.24 (also confirmed unchanged in 2026.3.31)
- Channel: msteams (bundled plugin)
Root Cause Analysis
After tracing through the bundled plugin code, the issue is in how the msteams plugin constructs session keys for channel messages.
normalizeMSTeamsConversationId() strips the thread-identifying ;messageid=XXX suffix from the Teams conversation ID:
function normalizeMSTeamsConversationId(raw) {
return raw.split(";")[0] ?? raw;
}
Teams delivers thread replies with conversation IDs like:
19:xxx@thread.tacv2;messageid=1234567890
After normalization, this becomes just:
19:xxx@thread.tacv2
This normalized (channel-level) ID is then passed to resolveAgentRoute() as peer.id, so all threads in a channel share the same session key: agent:main:msteams:channel:19:xxx@thread.tacv2.
extractMSTeamsConversationMessageId() does correctly extract the thread-specific message ID — but it's only used for Graph API media URL construction, never for session routing.
The core session module already has resolveThreadSessionKeys() which appends :thread:{threadId} to a base session key, but the msteams plugin never calls it.
Expected Behavior
Each Teams channel thread should get its own session key, e.g.:
agent:main:msteams:channel:19:xxx@thread.tacv2:thread:1234567890
The resolveThreadSessionKeys() function in session-key-*.js already supports this pattern — the msteams plugin just needs to call it with the conversationMessageId after route resolution.
Steps to Reproduce
- Configure the msteams channel plugin
- In a Teams channel, create Thread A and ask the bot a question
- Create Thread B and ask the bot a different question
- Observe that responses in Thread B reference context from Thread A
Additional Context
session.threadBindings.enabled: true is set in config but has no effect since the msteams plugin doesn't extract/pass thread IDs
historyLimit also contributes to bleed since it loads the last N messages from the channel (across all threads), but the session key issue is the primary cause
- The
resolveAgentRoute() function doesn't accept a threadId parameter, so the fix likely needs to happen after route resolution in the msteams message handler
Bug type
Behavior bug (incorrect output/state without crash)
Summary
When users post in different threads within the same Teams channel, the bot mixes conversation context between threads. A question in Thread A includes context from Thread B. Each thread should have its own isolated session.
Version
Root Cause Analysis
After tracing through the bundled plugin code, the issue is in how the msteams plugin constructs session keys for channel messages.
normalizeMSTeamsConversationId()strips the thread-identifying;messageid=XXXsuffix from the Teams conversation ID:Teams delivers thread replies with conversation IDs like:
19:xxx@thread.tacv2;messageid=1234567890After normalization, this becomes just:
19:xxx@thread.tacv2This normalized (channel-level) ID is then passed to
resolveAgentRoute()aspeer.id, so all threads in a channel share the same session key:agent:main:msteams:channel:19:xxx@thread.tacv2.extractMSTeamsConversationMessageId()does correctly extract the thread-specific message ID — but it's only used for Graph API media URL construction, never for session routing.The core session module already has
resolveThreadSessionKeys()which appends:thread:{threadId}to a base session key, but the msteams plugin never calls it.Expected Behavior
Each Teams channel thread should get its own session key, e.g.:
agent:main:msteams:channel:19:xxx@thread.tacv2:thread:1234567890The
resolveThreadSessionKeys()function insession-key-*.jsalready supports this pattern — the msteams plugin just needs to call it with theconversationMessageIdafter route resolution.Steps to Reproduce
Additional Context
session.threadBindings.enabled: trueis set in config but has no effect since the msteams plugin doesn't extract/pass thread IDshistoryLimitalso contributes to bleed since it loads the last N messages from the channel (across all threads), but the session key issue is the primary causeresolveAgentRoute()function doesn't accept athreadIdparameter, so the fix likely needs to happen after route resolution in the msteams message handler