Skip to content

fix(msteams): release failed Graph collection bodies#109970

Open
RileyJJY wants to merge 1 commit into
openclaw:mainfrom
RileyJJY:jjy/fix/msteams-graph-collection-body
Open

fix(msteams): release failed Graph collection bodies#109970
RileyJJY wants to merge 1 commit into
openclaw:mainfrom
RileyJJY:jjy/fix/msteams-graph-collection-body

Conversation

@RileyJJY

Copy link
Copy Markdown
Contributor

What Problem This Solves

Fixes an issue where MS Teams Graph hosted-content collection lookups could leave a non-OK response body open while falling back to an empty media result.

Why This Change Was Made

The Graph collection fallback behavior is unchanged: non-OK collection responses still produce an empty item list with the observed status. The fallback branch now cancels any response body before returning.

User Impact

MS Teams media handling keeps skipping unavailable hosted-content collections while releasing failed Graph collection response streams promptly.

Evidence

  • Production module boundary: ./node_modules/.bin/tsx proof-msteams-graph-collection.ts imported the real downloadMSTeamsGraphMedia entrypoint, exercised a 404 hostedContents collection response, and used a 200 empty collection negative-control response.
  • node scripts/run-vitest.mjs extensions/msteams/src/attachments.graph.test.ts -- -t "cancels non-OK Graph collection bodies"
  • node scripts/run-vitest.mjs extensions/msteams/src/attachments.graph.test.ts
$ ./node_modules/.bin/tsx proof-msteams-graph-collection.ts
fallback-media-count=0
fallback-hosted-status=404
fallback-body-canceled=true
success-media-count=0
success-hosted-status=200
proof-msteams-graph-collection.ts
import { downloadMSTeamsGraphMedia } from "./extensions/msteams/src/attachments/graph.ts";

const messageUrl = "https://example.com/v1.0/chats/19%3Achat/messages/123";

function cancelTrackedResponse(text: string, init: ResponseInit) {
  let canceled = false;
  const stream = new ReadableStream<Uint8Array>({
    start(controller) {
      controller.enqueue(new TextEncoder().encode(text));
    },
    cancel() {
      canceled = true;
    },
  });
  return {
    response: new Response(stream, init),
    wasCanceled: () => canceled,
  };
}

const missingHosted = cancelTrackedResponse("missing hosted contents", { status: 404 });
const missingFetch = async (input: RequestInfo | URL) => {
  const url = String(input);
  if (url === messageUrl) {
    return new Response(JSON.stringify({ attachments: [] }), { status: 200 });
  }
  if (url === `${messageUrl}/hostedContents`) {
    return missingHosted.response;
  }
  return new Response("unexpected", { status: 404 });
};
(missingFetch as typeof missingFetch & { mock?: object }).mock = {};

const fallback = await downloadMSTeamsGraphMedia({
  messageUrl,
  tokenProvider: { getAccessToken: async () => "token" },
  maxBytes: 1024 * 1024,
  allowHosts: ["example.com"],
  authAllowHosts: ["example.com"],
  fetchFn: missingFetch as typeof fetch,
  resolveFn: async () => ({ address: "93.184.216.34" }),
});
console.log(`fallback-media-count=${fallback.media.length}`);
console.log(`fallback-hosted-status=${fallback.hostedStatus}`);
console.log(`fallback-body-canceled=${missingHosted.wasCanceled()}`);

const successFetch = async (input: RequestInfo | URL) => {
  const url = String(input);
  if (url === messageUrl) {
    return new Response(JSON.stringify({ attachments: [] }), { status: 200 });
  }
  if (url === `${messageUrl}/hostedContents`) {
    return new Response(JSON.stringify({ value: [] }), { status: 200 });
  }
  return new Response("unexpected", { status: 404 });
};
(successFetch as typeof successFetch & { mock?: object }).mock = {};

const success = await downloadMSTeamsGraphMedia({
  messageUrl,
  tokenProvider: { getAccessToken: async () => "token" },
  maxBytes: 1024 * 1024,
  allowHosts: ["example.com"],
  authAllowHosts: ["example.com"],
  fetchFn: successFetch as typeof fetch,
  resolveFn: async () => ({ address: "93.184.216.34" }),
});
console.log(`success-media-count=${success.media.length}`);
console.log(`success-hosted-status=${success.hostedStatus}`);

AI-assisted: built with Codex

@openclaw-barnacle openclaw-barnacle Bot added channel: msteams Channel integration: msteams size: XS labels Jul 17, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. labels Jul 17, 2026
@clawsweeper

clawsweeper Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 25, 2026, 3:11 PM ET / 19:11 UTC.

ClawSweeper review

What this changes

The PR cancels a failed Microsoft Graph hosted-content collection response body before returning the existing empty-media fallback, and adds a regression test.

Merge readiness

⚠️ Ready for maintainer review - 3 items remain

The supplied PR evidence supports a narrow Microsoft Teams resource-cleanup fix, but the checkout could not be read in this review run, so current-main behavior, surrounding call paths, and feature history could not be independently verified. Keep this PR open for ordinary maintainer review of its current clean merge result.

Priority: P2
Reviewed head: e84893e4f1557279ae3c6a69bcad53a9a0228429

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) The PR presents focused real behavior proof and a narrowly scoped implementation, with final confidence limited by the need to review the current merge result.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The PR body includes redaction-safe terminal output from the real downloader entrypoint showing the failed collection fallback still returns no media and that the response body is canceled; focused regression-test commands are supplemental.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The PR body includes redaction-safe terminal output from the real downloader entrypoint showing the failed collection fallback still returns no media and that the response body is canceled; focused regression-test commands are supplemental.
Evidence reviewed 4 items Proposed runtime repair: The PR adds an awaited cancellation of a non-OK Graph collection response body before returning the pre-existing empty-items fallback.
Regression coverage: The PR adds a tracked ReadableStream test that asserts a 404 hosted-content collection response returns empty media and its body cancellation callback runs.
Contributor real-behavior proof: The PR body records a real-entrypoint terminal run for a 404 collection response with fallback-media-count=0, fallback-hosted-status=404, and fallback-body-canceled=true, plus a 200 empty-collection control.
Findings None None.
Security None None.

How this fits together

The Microsoft Teams plugin downloads a message's Graph attachments and hosted-content collections to produce media for inbound messages. A failed collection lookup currently falls back to no hosted media; this change releases that unused response stream before the fallback returns.

flowchart LR
  A[Teams message] --> B[Graph media downloader]
  B --> C[Hosted-content collection request]
  C --> D{Collection response OK?}
  D -->|Yes| E[Parse media items]
  D -->|No| F[Cancel response body]
  F --> G[Return empty hosted media]
  E --> H[Attachment media result]
  G --> H
Loading

Before merge

  • Resolve merge risk (P1) - The branch is behind current main; although GitHub reports it mergeable, a maintainer should refresh the current merge result and re-check the surrounding Graph collection error paths before landing.
  • Resolve merge risk (P1) - Current-main and git-history inspection was unavailable in this run, so this review cannot rule out an already-landed equivalent cleanup or identify the established Microsoft Teams area owners.
  • Complete next step (P2) - No concrete automated repair is identified; the remaining action is ordinary maintainer review after refreshing the branch against current main.
Agent review details

Security

None.

PR surface

Source +1, Tests +47. Total +48 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 1 0 +1
Tests 1 47 0 +47
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 48 0 +48

Review metrics

None.

Stored data model

Persistent data-model change detected: vector/embedding metadata: extensions/msteams/src/attachments.graph.test.ts. Confirm migration or upgrade compatibility proof before merge.

Merge-risk options

Maintainer options:

  1. Decide the mitigation before merge
    Land a refreshed, focused cleanup only if the current merge result still preserves the existing non-OK fallback and the added test covers the current Graph collection helper.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Technical review

Best possible solution:

Land a refreshed, focused cleanup only if the current merge result still preserves the existing non-OK fallback and the added test covers the current Graph collection helper.

Do we have a high-confidence way to reproduce the issue?

Unclear for an unfixed current-main checkout: the supplied real-entrypoint proof demonstrates the intended 404 path after the patch, while the patch and regression test make the prior unclosed-body path source-reproducible.

Is this the best way to solve the issue?

Unclear: canceling the unused non-OK response body at the collection helper is a narrow and plausible owner-boundary fix, but current-main callers and sibling response-consumption paths were not independently available for comparison.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 0e2c60e605d0.

Labels

Label justifications:

  • P2: This is a bounded Microsoft Teams media-resource cleanup with limited user-facing blast radius.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes redaction-safe terminal output from the real downloader entrypoint showing the failed collection fallback still returns no media and that the response body is canceled; focused regression-test commands are supplemental.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes redaction-safe terminal output from the real downloader entrypoint showing the failed collection fallback still returns no media and that the response body is canceled; focused regression-test commands are supplemental.

Evidence

What I checked:

  • Proposed runtime repair: The PR adds an awaited cancellation of a non-OK Graph collection response body before returning the pre-existing empty-items fallback. (extensions/msteams/src/attachments/graph.ts:120, e84893e4f155)
  • Regression coverage: The PR adds a tracked ReadableStream test that asserts a 404 hosted-content collection response returns empty media and its body cancellation callback runs. (extensions/msteams/src/attachments.graph.test.ts:345, e84893e4f155)
  • Contributor real-behavior proof: The PR body records a real-entrypoint terminal run for a 404 collection response with fallback-media-count=0, fallback-hosted-status=404, and fallback-body-canceled=true, plus a 200 empty-collection control. (e84893e4f155)
  • Current review limitation: Read-only shell inspection failed before repository files, scoped guidance, git history, and current-main source could be independently examined; this review therefore does not claim that current main lacks or already contains the repair.

Likely related people:

  • RileyJJY: The available PR metadata attributes the current proposed runtime and regression-test change to this contributor; no current-main history could be read to establish stronger ownership routing. (role: recent contributor to the exact Graph-media path; confidence: low; commits: e84893e4f155; files: extensions/msteams/src/attachments/graph.ts, extensions/msteams/src/attachments.graph.test.ts)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Refresh the branch against current main and have a Microsoft Teams area reviewer confirm the current Graph collection helper remains the correct cleanup boundary.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (4 earlier review cycles)
  • reviewed 2026-07-17T12:29:33.760Z sha 1677e8b :: needs maintainer review before merge. :: none
  • reviewed 2026-07-17T23:42:43.930Z sha 1d89e67 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-20T08:07:31.870Z sha bcba75e :: needs maintainer review before merge. :: none
  • reviewed 2026-07-20T08:43:57.620Z sha bcba75e :: needs maintainer review before merge. :: none

@RileyJJY
RileyJJY force-pushed the jjy/fix/msteams-graph-collection-body branch from 1677e8b to 1d89e67 Compare July 17, 2026 23:38
@RileyJJY
RileyJJY force-pushed the jjy/fix/msteams-graph-collection-body branch 2 times, most recently from 868fed5 to 9203b21 Compare July 20, 2026 07:18
@RileyJJY
RileyJJY force-pushed the jjy/fix/msteams-graph-collection-body branch 2 times, most recently from bcba75e to a0fd950 Compare July 21, 2026 14:35
@RileyJJY
RileyJJY force-pushed the jjy/fix/msteams-graph-collection-body branch from a0fd950 to 514c647 Compare July 23, 2026 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: msteams Channel integration: msteams P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: XS status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants