Summary
File attachments (PDF, documents) sent in Teams personal DM chats are silently dropped. The agent receives <media:document> placeholder tags but no actual file content. Inline images in DMs may also be affected.
Root Cause
buildMSTeamsGraphMessageUrls() in inbound-media.ts uses the Bot Framework conversationId (format a:1dRsHC...) directly as the Graph API chatId parameter when constructing the Graph message URL:
https://graph.microsoft.com/v1.0/chats/{conversationId}/messages/{messageId}
However, the Bot Framework conversation ID for personal chats (a:... format) is not a valid Graph API chat ID. Graph API expects a different identifier (typically 19:...@thread.v2 or an opaque GUID format). The Graph API returns:
{
"error": {
"code": "NotFound",
"message": "NotFound",
"innerError": {
"code": "1",
"message": "Invalid ThreadId."
}
}
}
This error is caught silently, resulting in "graph media fetch empty" in the debug logs with no visible error to the user or agent.
Reproduction
- Configure MS Teams channel with a bot registration that has
Chat.Read.All and ChatMessage.Read.All (Application permissions, admin consented)
- Send a PDF or document file to the bot in a personal (1:1) DM
- Observe: agent receives
<media:document> tag but no file content
- Gateway log shows:
"graph media fetch empty" (DEBUG level)
Diagnostic Evidence
Bot Framework conversation ID (from msteams-conversations.json):
a:1dRsHCobZ1AxURzY05DcePY5NAOLanRc1nZgZ6frWzO8yB0XwH_DWytw_K2yiLRd8UpiKUgveWHCHPvJHV6sAirvHbZGI1ZARQN_eUNr32zD5AwOdySFgIVGsvGHGztN1
Graph API response when using this ID:
GET /v1.0/chats/{above_id}/messages/{msgId} → 404 NotFound, "Invalid ThreadId"
Graph API token is valid — independently verified by obtaining a client_credentials token and calling other Graph endpoints successfully.
Azure Bot permissions confirmed: Chat.Read.All + ChatMessage.Read.All (Application, admin consented). Client secret valid (expires 2027-02).
Additional Context
Expected Fix
The Graph message URL builder should translate the Bot Framework a: conversation ID to the corresponding Graph API chat ID before constructing the URL. Microsoft's Bot Framework SDK may provide a mapping, or the Graph /chats endpoint could be queried to find the correct ID.
Alternatively, for personal DM file attachments, the Bot Framework activity itself may contain direct download URLs (via activity.attachments[].contentUrl) that could be fetched using the bot token instead of requiring the Graph API path.
Environment
- OpenClaw: 2026.4.5 (3e72c03) — also reproduced on 2026.4.2
- Node: v24.13.1
- Platform: Ubuntu 24.04 (Linux 6.17.0-1009-aws, x64)
- Channel: MS Teams (Bot Framework)
- Chat type: Personal (1:1 DM)
Workaround
Upload files via alternative methods (OneDrive link, email, SCP to server) instead of Teams DM file attachments.
Summary
File attachments (PDF, documents) sent in Teams personal DM chats are silently dropped. The agent receives
<media:document>placeholder tags but no actual file content. Inline images in DMs may also be affected.Root Cause
buildMSTeamsGraphMessageUrls()ininbound-media.tsuses the Bot FrameworkconversationId(formata:1dRsHC...) directly as the Graph APIchatIdparameter when constructing the Graph message URL:However, the Bot Framework conversation ID for personal chats (
a:...format) is not a valid Graph API chat ID. Graph API expects a different identifier (typically19:...@thread.v2or an opaque GUID format). The Graph API returns:{ "error": { "code": "NotFound", "message": "NotFound", "innerError": { "code": "1", "message": "Invalid ThreadId." } } }This error is caught silently, resulting in
"graph media fetch empty"in the debug logs with no visible error to the user or agent.Reproduction
Chat.Read.AllandChatMessage.Read.All(Application permissions, admin consented)<media:document>tag but no file content"graph media fetch empty"(DEBUG level)Diagnostic Evidence
Bot Framework conversation ID (from msteams-conversations.json):
Graph API response when using this ID:
Graph API token is valid — independently verified by obtaining a client_credentials token and calling other Graph endpoints successfully.
Azure Bot permissions confirmed:
Chat.Read.All+ChatMessage.Read.All(Application, admin consented). Client secret valid (expires 2027-02).Additional Context
The
"graph media fetch empty"log message fires on every inbound Teams message (not just file uploads), because all Teams messages havetext/htmlattachments that trigger the Graph media fallback path. For text-only messages this is harmless (no media to download), but for file uploads it means the files are lost.The debug logger at this call site only serializes the first two positional arguments, so the
{ attempts }diagnostic object (containinghostedStatus,attachmentStatus,tokenError, etc.) is silently dropped from the log output. This was also noted in MS Teams: startup resolution stores Graph group GUID as team key, but Bot Framework sends General channel conversation ID — all channel messages silently dropped #41390.Issue MS Teams: startup resolution stores Graph group GUID as team key, but Bot Framework sends General channel conversation ID — all channel messages silently dropped #41390 describes the same fundamental problem (Bot Framework IDs ≠ Graph API IDs) but for team/channel routing. This issue is the personal DM media download variant.
Expected Fix
The Graph message URL builder should translate the Bot Framework
a:conversation ID to the corresponding Graph API chat ID before constructing the URL. Microsoft's Bot Framework SDK may provide a mapping, or the Graph/chatsendpoint could be queried to find the correct ID.Alternatively, for personal DM file attachments, the Bot Framework activity itself may contain direct download URLs (via
activity.attachments[].contentUrl) that could be fetched using the bot token instead of requiring the Graph API path.Environment
Workaround
Upload files via alternative methods (OneDrive link, email, SCP to server) instead of Teams DM file attachments.