Skip to content

[Bug]: Teams app removal/re-add retains prior DM session history #99054

Description

@jimmypuckett

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

After a user removed and re-added a Microsoft Teams bot/app in a personal chat, OpenClaw resumed the same server-side DM session and the bot could answer from pre-removal conversation history.

Steps to reproduce

  1. Configure the Microsoft Teams channel with a personal-scope bot/app and a Teams user.
  2. Have the user send a direct message to the bot.
  3. Confirm OpenClaw creates or updates a direct-message session entry.
  4. In Teams, remove the bot/app from the personal chat using the app/chat removal UI.
  5. Re-add the same bot/app for the same user.
  6. Send another direct message to the bot.
  7. Inspect the OpenClaw session entry and transcript used for the new message.

Observed case, redacted:

  • OpenClaw direct-message session key: the same key before and after Teams app removal/re-add
  • OpenClaw session id: the same session id before and after Teams app removal/re-add
  • Transcript: the same transcript file appended the post-readd exchange

Expected behavior

After a Teams bot/app is removed from a personal chat and later re-added, OpenClaw should not silently continue the prior active DM session in a way that lets the bot reason over pre-removal chat history without an explicit retention decision.

At minimum, OpenClaw should handle or explicitly ignore the relevant Teams lifecycle signal. Microsoft documents installationUpdate remove/add events as the place where bots can meet privacy/data-retention requirements and clean up user or thread data when uninstalled.

There is existing OpenClaw precedent for treating channel lifecycle or explicit channel reset boundaries as session boundaries without deleting transcripts:

  • Discord marks sessions for an archived thread as stale by setting updatedAt to 0 in extensions/discord/src/monitor/thread-session-close.ts; the comment says the next inbound message starts a fresh conversation without deleting on-disk transcript history.
  • Telegram treats /new and non-soft /reset as session boundary commands in extensions/telegram/src/message-cache.ts, and extensions/telegram/src/message-cache.test.ts verifies that context before the current reset boundary is excluded.

Actual behavior

The re-added bot continued using the same OpenClaw session key and session id.

Evidence from the observed test, redacted:

  • Before removal/re-add, the direct-message session key pointed to <same-session-id>.
  • After re-add, the same direct-message session key still pointed to <same-session-id>.
  • The same JSONL transcript appended the new post-readd exchange.
  • A follow-up prompt asking about the earlier conversation caused the bot to use the old OpenClaw session history.

This is actionable at the source level: current Teams handling does not dispatch installationUpdate, and conversationUpdate handling only covers membersAdded. A PR can use sanitized lifecycle fixtures for the dispatch/handling gap, then use one live Teams proof run to confirm which lifecycle signal the Teams removal UI emits.

OpenClaw version

Observed with @openclaw/[email protected] in a Microsoft Teams test deployment.

Operating system

macOS for the observed test. The current upstream source-level lifecycle dispatch gap is OS-independent.

Install method

npm

Model

Not applicable. The issue is the Teams channel/session lifecycle boundary before model choice matters.

Provider / routing chain

Microsoft Teams personal chat -> Bot Framework webhook -> OpenClaw msteams channel -> OpenClaw direct-message session.

Additional provider/model setup details

No provider-specific behavior is known to be involved. The retained session entry can be observed at the session store/transcript layer.

Logs

Current upstream source facts:

extensions/msteams/src/monitor.ts dispatches message, conversationUpdate, and messageReaction in the ActivityHandler shim, but not installationUpdate.

extensions/msteams/src/monitor.ts routes conversationUpdate only to membersAdded handlers.

extensions/msteams/src/monitor-handler.ts reads ctx.activity.membersAdded for welcome-card behavior and does not inspect membersRemoved.

extensions/msteams/src/sdk-types.ts includes membersRemoved?: Array<{ id?: string; name?: string }>, but no Teams handler currently uses it.

extensions/msteams/src/monitor-handler/message-handler.ts resolves the direct-message route/session before dispatch. In the observed case, the same direct-message session key remained active across Teams app removal/re-add.

Relevant precedent in other channels:

extensions/discord/src/monitor/listeners.ts calls closeDiscordThreadSessions when a Discord thread transitions to archived.

extensions/discord/src/monitor/thread-session-close.ts marks matching Discord thread session entries stale by setting updatedAt to 0, preserving transcript files.

extensions/telegram/src/message-cache.ts recognizes /new and non-soft /reset as session boundary commands.

extensions/telegram/src/send.ts wraps membership-related 403 errors (bot not a member, blocked, kicked) with actionable context instead of treating them as ordinary send failures.

Screenshots, recordings, and evidence

  • Redacted observed evidence shows the same direct-message session key and session id before and after Teams app removal/re-add.
  • Supporting screenshots:
    • eams presents the personal chat as "You're starting a new conversation" while the user types a prompt asking for prior conversation history.
Image
  • The bot responds with details from the pre-removal conversation history despite the Teams new-conversation UI state.
Image
  • Microsoft Teams documentation says installationUpdate is emitted on install/uninstall, uses action: add or action: remove, and can be used for privacy/data-retention cleanup.
  • Microsoft Teams bot status-code documentation says:
    • BotNotInConversationRoster: bot is not in the conversation; wait for installationUpdate before sending again.
    • ConversationBlockedByUser: delete the cached conversation and stop attempting posts until the user explicitly interacts again.
  • OpenClaw already uses a non-destructive stale-session pattern in Discord thread lifecycle handling and explicit session-boundary handling in Telegram.

Impact and severity

Affected: Microsoft Teams personal-chat users of OpenClaw bots.

Severity: Medium/high privacy and session-state issue. The bot can continue with prior server-side session history after a user removes and re-adds the Teams app/bot.

Frequency: Observed in one Teams personal app removal/re-add flow. Broader frequency has not been measured.

Consequence: A user may reasonably believe removing a Teams bot/app ended or reset that bot relationship, while OpenClaw can continue using the prior server-side conversation session after re-add.

Additional information

This issue is intentionally narrow:

  • It is not asking OpenClaw to hard-delete transcript files by default.
  • It is not about Teams message delete/edit lifecycle.
  • It is not asking for broad Teams command support.
  • It is not proposing a session-key derivation change yet.

Likely safe implementation direction, if maintainers agree with the expected behavior:

  1. Dispatch installationUpdate events in the Teams ActivityHandler shim.
  2. Add focused handling for installationUpdate.action === "remove" and action === "add".
  3. Add focused handling for conversationUpdate.membersRemoved where the removed member is the bot, if Teams emits that shape for any remove path.
  4. For personal-scope remove/uninstall, reset or mark stale only the matching Teams direct-message session for the current personal chat, using existing session lifecycle APIs rather than deleting transcript files directly.
  5. For outbound BotNotInConversationRoster and ConversationBlockedByUser, clear/tombstone cached conversation references and avoid proactive sends until a fresh install event or user interaction.

Suggested proof for a PR:

  • Sanitized fixtures/tests for installationUpdate add/remove.
  • Sanitized fixture/test for conversationUpdate.membersRemoved.
  • Regression showing current main ignores installationUpdate.
  • Test that normal membersAdded welcome behavior still works.
  • Test that unknown Teams lifecycle events return 200 without throwing.
  • Live Teams proof, if available, showing whether the remove/re-add UI emits installationUpdate, conversationUpdate.membersRemoved, neither, or both.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.bugSomething isn't workingbug:behaviorIncorrect behavior without a crashclawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:securitySecurity boundary, credential, authz, sandbox, or sensitive-data risk.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions