Skip to content

msteams: fix DM image delivery + user target routing#18716

Open
ktsushilofficial wants to merge 5 commits intoopenclaw:mainfrom
ktsushilofficial:fix/msteams-dm-image-routing
Open

msteams: fix DM image delivery + user target routing#18716
ktsushilofficial wants to merge 5 commits intoopenclaw:mainfrom
ktsushilofficial:fix/msteams-dm-image-routing

Conversation

@ktsushilofficial
Copy link
Copy Markdown

@ktsushilofficial ktsushilofficial commented Feb 17, 2026

Summary

  • Problem: Two MSTeams issues - (1) target=user: returns 403 BotNotInConversationRoster, (2) Images silently dropped in DM
  • Why it matters: Users cannot send messages to DMs or send images via MSTeams
  • What changed: Modified conversation lookup to prefer "personal" type, added buffer parameter support for inline base64 media
  • What did NOT change: No other channels, no config changes, no API surface changes beyond MSTeams

Change Type

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • DM messages with target=user: now route correctly to personal conversations
  • Inline base64 images now delivered properly to MSTeams DMs

Security Impact

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No (added parameter support, no new capabilities)
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Linux (Ubuntu 24.04)
  • Runtime: Node 22
  • Integration: MSTeams

Steps

  1. Send message with target=user:
  2. Observe 403 error before fix, success after
  3. Send message with buffer/base64 image to DM
  4. Observe silent drop before fix, image delivered after

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Failure Recovery

  • How to disable/revert: git revert the commit
  • Files to restore: 5 files changed

Risks and Mitigations

  • None - minimal fix targeting specific MSTeams issues

Greptile Summary

This PR fixes two MSTeams issues: (1) target=user: routing returning 403 BotNotInConversationRoster errors by preferring "personal" conversation references over stale group/channel ones, and (2) adding buffer parameter support for inline base64 media delivery in DMs.

  • User routing fix (send-context.ts): The conversation lookup now filters all stored references for a user and prefers "personal" type conversations, which work reliably for proactive messaging. This correctly addresses the 403 error when the bot has both personal and group conversation references for the same user.
  • Buffer/media support (send.ts, outbound.ts, types): Adds a buffer string parameter through the outbound adapter pipeline, converting base64 data to a data URL format for media delivery.
  • Critical issue: The buffer-to-data-URL conversion in send.ts passes the resulting data:image/...;base64,... string to loadWebMedia(), which does not handle data URLs (only HTTP URLs and local file paths). This will cause a runtime error when buffer is provided without mediaUrl, meaning the DM image delivery fix will not work as intended.
  • Incomplete pipeline wiring: The buffer field is added to ChannelOutboundContext but createChannelOutboundContextBase() in deliver.ts never populates it, so buffer data cannot flow through the standard delivery pipeline.

Confidence Score: 2/5

  • The user routing fix is sound, but the buffer/media delivery path has a runtime error that needs to be resolved before merging.
  • Score of 2 reflects that half the PR (user routing fix) is correct and safe, while the other half (buffer/base64 media delivery) will fail at runtime due to passing data URLs to loadWebMedia() which doesn't support them. The delivery pipeline also doesn't wire buffer data through to the adapter.
  • extensions/msteams/src/send.ts needs the most attention — the buffer-to-loadWebMedia path will throw. src/infra/outbound/deliver.ts also needs attention to wire buffer through createChannelOutboundContextBase.

Last reviewed commit: 0acbbc6

(1/5) You can manually trigger the agent by mentioning @greptileai in a comment!

Issue 1: target=user: returns 403
- When looking up conversation by user ID, now prefers 'personal' conversation
  type over stale group/channel references that cause BotNotInConversationRoster

Issue 2: Images silently dropped in DM
- Added buffer parameter support to outbound adapter and send function
- Base64 buffer data now properly converted to inline media URL
- Fixes openclaw#18693
@openclaw-barnacle openclaw-barnacle bot added channel: msteams Channel integration: msteams size: S labels Feb 17, 2026
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 17, 2026

Additional Comments (2)

extensions/msteams/src/send.ts
Data URL passed to loadWebMedia will throw at runtime

When buffer is provided without mediaUrl, this code converts it to a data URL (e.g., data:image/png;base64,...) and assigns it to resolvedMediaUrl. On line 142, resolvedMediaUrl is then passed to loadWebMedia().

However, loadWebMedia() (in src/web/media.ts) does not handle data URLs — it only supports HTTP/HTTPS URLs and local file paths. A data URL will fall through to the local file path branch and fail on fs.readFile() or assertLocalMediaAllowed().

The buffer-to-data-URL conversion should instead parse the base64 data directly into a Buffer and skip the loadWebMedia call entirely (similar to how the existing personal-chat image path works downstream at lines 202-208, where it converts media back to base64 after loading). Consider decoding the buffer inline and constructing the media object directly rather than round-tripping through loadWebMedia.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/msteams/src/send.ts
Line: 116:142

Comment:
**Data URL passed to `loadWebMedia` will throw at runtime**

When `buffer` is provided without `mediaUrl`, this code converts it to a data URL (e.g., `data:image/png;base64,...`) and assigns it to `resolvedMediaUrl`. On line 142, `resolvedMediaUrl` is then passed to `loadWebMedia()`.

However, `loadWebMedia()` (in `src/web/media.ts`) does not handle data URLs — it only supports HTTP/HTTPS URLs and local file paths. A data URL will fall through to the local file path branch and fail on `fs.readFile()` or `assertLocalMediaAllowed()`.

The buffer-to-data-URL conversion should instead parse the base64 data directly into a `Buffer` and skip the `loadWebMedia` call entirely (similar to how the existing personal-chat image path works downstream at lines 202-208, where it converts media back to base64 after loading). Consider decoding the buffer inline and constructing the media object directly rather than round-tripping through `loadWebMedia`.

How can I resolve this? If you propose a fix, please make it concise.

extensions/msteams/src/send-context.ts
Replaces targeted findByUserId with full store scan

The old code used store.findByUserId(recipient.id) which iterated conversations until it found a match. The new code calls store.list() to load all conversations (up to 1000) and then filters them in-memory.

While the store is capped at 1000 entries and this won't cause performance issues in practice, the findByUserId method already performs the same user-matching logic (aadObjectId / id comparison) in conversation-store-fs.ts:116-131. Consider extending the store interface with a method like findAllByUserId that returns all matches for a user, rather than bypassing findByUserId and re-implementing its filtering logic at the call site. This keeps the search logic encapsulated in the store.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/msteams/src/send-context.ts
Line: 91:95

Comment:
**Replaces targeted `findByUserId` with full store scan**

The old code used `store.findByUserId(recipient.id)` which iterated conversations until it found a match. The new code calls `store.list()` to load *all* conversations (up to 1000) and then filters them in-memory.

While the store is capped at 1000 entries and this won't cause performance issues in practice, the `findByUserId` method already performs the same user-matching logic (`aadObjectId` / `id` comparison) in `conversation-store-fs.ts:116-131`. Consider extending the store interface with a method like `findAllByUserId` that returns all matches for a user, rather than bypassing `findByUserId` and re-implementing its filtering logic at the call site. This keeps the search logic encapsulated in the store.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@BradGroux BradGroux self-assigned this Mar 10, 2026
@BradGroux
Copy link
Copy Markdown
Contributor

Hi @ktsushilofficial — thanks for the submission. I’m the new Microsoft Teams maintainer for OpenClaw. Please give me a day or two to work through the open Teams backlog. Also, join the Twitter community for daily MS Teams feedback + updates: https://x.com/i/communities/2031170403607974228

1 similar comment
@BradGroux
Copy link
Copy Markdown
Contributor

Hi @ktsushilofficial — thanks for the submission. I’m the new Microsoft Teams maintainer for OpenClaw. Please give me a day or two to work through the open Teams backlog. Also, join the Twitter community for daily MS Teams feedback + updates: https://x.com/i/communities/2031170403607974228

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: msteams Channel integration: msteams size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug Report: MSTeams message tool - DM image delivery fails + user target routing broken

3 participants