Environment
- OpenClaw: 2026.2.25 (latest)
- OS: Ubuntu 22.04 (x64), Node v24.13.1
- Azure Bot: Single-tenant, MS Teams channel enabled
- Permission:
Chat.Read.All (Application) granted with admin consent
Problem
When a user sends an inline image (paste/screenshot) in a 1:1 Teams chat with the bot, the msteams plugin detects the image but fails to download it.
Logs show:
"html attachment summary"
"graph media fetch empty"
"inline images detected but none downloaded"
Root Cause
resolveMSTeamsInboundMedia() in src/monitor-handler/inbound-media.ts passes the Bot Framework turn context tokenProvider to downloadMSTeamsGraphMedia(). This provider calls getAccessToken("https://graph.microsoft.com") but returns a Bot Connector-scoped token, not a Graph API token with Chat.Read.All scope.
Meanwhile, resolveGraphToken() in src/graph.ts correctly uses MsalTokenProvider with the bot's appId/appPassword to get a properly-scoped Graph token via client credentials flow. This code path is not used by the inbound media handler.
Verified: Manual curl test using the same appId/appPassword with client credentials grant to https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token returns a valid Graph token. The credentials and permissions are correct — the issue is which token provider is used.
Suggested Fix
Have downloadMSTeamsGraphMedia() fall back to resolveGraphToken() (MSAL client credentials) when the bot adapter's tokenProvider.getAccessToken("https://graph.microsoft.com") fails or returns a token without Graph scopes.
The MSAL token acquisition code already exists in the same plugin (graph.ts:52-67) — it just needs to be wired into the inbound media path.
Workaround
File attachments (drag-and-drop) may use a different download path. Inline paste/screenshots in 1:1 chats are broken.
Environment
Chat.Read.All(Application) granted with admin consentProblem
When a user sends an inline image (paste/screenshot) in a 1:1 Teams chat with the bot, the msteams plugin detects the image but fails to download it.
Logs show:
Root Cause
resolveMSTeamsInboundMedia()insrc/monitor-handler/inbound-media.tspasses the Bot Framework turn contexttokenProvidertodownloadMSTeamsGraphMedia(). This provider callsgetAccessToken("https://graph.microsoft.com")but returns a Bot Connector-scoped token, not a Graph API token withChat.Read.Allscope.Meanwhile,
resolveGraphToken()insrc/graph.tscorrectly usesMsalTokenProviderwith the bot'sappId/appPasswordto get a properly-scoped Graph token via client credentials flow. This code path is not used by the inbound media handler.Verified: Manual
curltest using the sameappId/appPasswordwith client credentials grant tohttps://login.microsoftonline.com/{tenant}/oauth2/v2.0/tokenreturns a valid Graph token. The credentials and permissions are correct — the issue is which token provider is used.Suggested Fix
Have
downloadMSTeamsGraphMedia()fall back toresolveGraphToken()(MSAL client credentials) when the bot adapter'stokenProvider.getAccessToken("https://graph.microsoft.com")fails or returns a token without Graph scopes.The MSAL token acquisition code already exists in the same plugin (
graph.ts:52-67) — it just needs to be wired into the inbound media path.Workaround
File attachments (drag-and-drop) may use a different download path. Inline paste/screenshots in 1:1 chats are broken.