Skip to content

[msteams] Inbound file attachments silently fail in DMs — file.download.info downloadUrl not rewritten to Graph shares endpoint #67177

@cwatts-sage

Description

@cwatts-sage

Bug type

Regression (worked before, now fails silently)

Summary

When a user sends a file attachment (drag-and-drop or attach from device) in a 1:1 Teams DM with the bot, the file is not downloaded. The agent receives a <media:document> (2 files) placeholder but no actual file content. No errors are logged — all failures are silently swallowed by catch {} blocks.

Steps to reproduce

  1. Configure OpenClaw with msteams channel (Bot Framework, single tenant)
  2. Set mediaAllowHosts: ["*"] and mediaAuthAllowHosts: ["*"]
  3. In a 1:1 Teams DM with the bot, drag-and-drop a file (e.g. PDF, DOCX, XLSX)
  4. Agent receives <media:document> placeholder text only — no file path, no file content
  5. No new files appear in ~/.openclaw/media/inbound/

Root cause

In resolveDownloadCandidate() (extensions/msteams/src/attachments/download.ts), when the attachment has contentType === "application/vnd.microsoft.teams.file.download.info", the code extracts content.downloadUrl and uses it directly as the download URL.

This downloadUrl is a *.sharepoint.com URL (e.g. https://tenant-my.sharepoint.com/personal/user/Documents/file.pdf?...) that requires an authenticated session with SharePoint/Graph permissions.

The download flow then:

  1. fetchWithAuthFallback → unauthenticated fetch → 401/403
  2. Retry with Bot Framework token (https://api.botframework.com scope) → still 401 (token lacks SharePoint permissions)
  3. Silent catch {} in downloadMSTeamsAttachments → returns empty array
  4. Agent gets placeholder text only

Why the recent fix (PR #63942) does not cover this

PR #63942 (merged Apr 10, included in v2026.4.10) correctly rewrites SharePoint/OneDrive URLs via tryBuildGraphSharesUrlForSharedLink() — but only for the contentUrl path in resolveDownloadCandidate():

// contentUrl path — HAS the fix ✅
const sharesUrl = tryBuildGraphSharesUrlForSharedLink(contentUrl);
return { url: sharesUrl ?? contentUrl, ... };

The file.download.info branch returns content.downloadUrl directly without applying the same rewrite:

// file.download.info path — MISSING the fix ❌
if (contentType === "application/vnd.microsoft.teams.file.download.info") {
    const downloadUrl = att.content.downloadUrl;
    return { url: downloadUrl, ... };  // Raw SharePoint URL, no rewrite
}

Proposed fix

Apply tryBuildGraphSharesUrlForSharedLink() to downloadUrl in the file.download.info branch of resolveDownloadCandidate():

if (contentType === "application/vnd.microsoft.teams.file.download.info") {
    const downloadUrl = att.content.downloadUrl;
    const sharesUrl = tryBuildGraphSharesUrlForSharedLink(downloadUrl);
    return { url: sharesUrl ?? downloadUrl, fileHint, ... };
}

This ensures the SharePoint download URL is rewritten to graph.microsoft.com/v1.0/shares/{shareId}/driveItem/content, which the existing fetchWithAuthFallback can authenticate via the Graph scope token.

Additional issue: silent error swallowing

All catch {} blocks in the attachment download chain (downloadMSTeamsAttachments, downloadMSTeamsBotFrameworkAttachment, downloadAndStoreMSTeamsRemoteMedia) silently swallow errors and return empty results. This makes debugging impossible — there is zero log output when file downloads fail. Consider adding log.debug calls in these catch blocks.

Environment

  • OpenClaw version: 2026.4.10 (44e5b62)
  • Channel: msteams (Bot Framework, single tenant)
  • Config: mediaAllowHosts: ["*"], mediaAuthAllowHosts: ["*"]
  • Conversation type: 1:1 DM (conversation ID starts with a:)
  • SharePoint connectivity: confirmed reachable from container (DNS resolves, HTTPS connects)

Related issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions