openclaw - ✅(Solved) Fix Slack download-file fails silently on Node.js v25 — globalThis.fetch does not support dispatcher option [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#63393Fetched 2026-04-09 07:54:20
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Participants
Timeline (top)
cross-referenced ×1

Error Message

// This fails in Node.js v25: globalThis.fetch(url, { dispatcher: new undici.Agent(), redirect: 'manual' }) // TypeError: fetch failed

// This works: undici.fetch(url, { dispatcher: new undici.Agent(), redirect: 'manual' })

Root Cause

createSlackMediaFetch in extensions/slack/src/actions.ts calls globalThis.fetch() and passes through the dispatcher option from the SSRF guard's fetchWithSsrFGuard. In Node.js v25, globalThis.fetch does not support the dispatcher option (only undici.fetch does), causing a TypeError: fetch failed.

The error is silently swallowed by the catch {} in resolveSlackMedia, making the download return null with no logging.

Fix Action

Fix

Strip dispatcher from the options before passing to globalThis.fetch in createSlackMediaFetch:

- const { headers: initHeaders, redirect: _redirect, ...rest } = init ?? {};
+ const { headers: initHeaders, redirect: _redirect, dispatcher: _dispatcher, ...rest } = init ?? {};

The SSRF DNS validation still runs upstream in fetchWithSsrFGuard — the dispatcher was only needed for DNS pinning which already completed before the custom fetch is called.

PR fix notes

PR #63401: fix(slack): strip dispatcher option before passing to globalThis.fetch

Description (problem / solution / changelog)

Bug

Slack download-file silently fails on Node.js v25 because globalThis.fetch does not support the dispatcher option. The error is swallowed by the catch block.

Root Cause

In createSlackMediaFetch(), when dispatcher is in init and !isMockedFetch(globalThis.fetch) is true, it uses fetchWithRuntimeDispatcher (undici). But when the condition is false (e.g. mocked fetch), it falls back to globalThis.fetch with the dispatcher option still present — causing TypeError: fetch failed in Node.js v25.

Fix

Strip dispatcher (and redirect) from init options before passing to globalThis.fetch. Undici-backed path is unaffected since it uses fetchWithRuntimeDispatcher directly with the original options.

Fixes #63393

Changed files

  • extensions/slack/src/monitor/media.ts (modified, +3/-2)

Code Example

// This fails in Node.js v25:
globalThis.fetch(url, { dispatcher: new undici.Agent(), redirect: 'manual' })
// TypeError: fetch failed

// This works:
undici.fetch(url, { dispatcher: new undici.Agent(), redirect: 'manual' })

---

- const { headers: initHeaders, redirect: _redirect, ...rest } = init ?? {};
+ const { headers: initHeaders, redirect: _redirect, dispatcher: _dispatcher, ...rest } = init ?? {};
RAW_BUFFERClick to expand / collapse

Bug

message(action="download-file") for Slack files always returns "File could not be downloaded (not found, too large, or inaccessible)." even when the file exists and the bot token has files:read scope.

Root Cause

createSlackMediaFetch in extensions/slack/src/actions.ts calls globalThis.fetch() and passes through the dispatcher option from the SSRF guard's fetchWithSsrFGuard. In Node.js v25, globalThis.fetch does not support the dispatcher option (only undici.fetch does), causing a TypeError: fetch failed.

The error is silently swallowed by the catch {} in resolveSlackMedia, making the download return null with no logging.

Reproduction

  1. Have a Slack file attachment in any channel
  2. Call message(action="download-file", channel="slack", fileId="F...")
  3. Always returns error, even though files.info API and direct curl with the bot token both work fine
// This fails in Node.js v25:
globalThis.fetch(url, { dispatcher: new undici.Agent(), redirect: 'manual' })
// TypeError: fetch failed

// This works:
undici.fetch(url, { dispatcher: new undici.Agent(), redirect: 'manual' })

Fix

Strip dispatcher from the options before passing to globalThis.fetch in createSlackMediaFetch:

- const { headers: initHeaders, redirect: _redirect, ...rest } = init ?? {};
+ const { headers: initHeaders, redirect: _redirect, dispatcher: _dispatcher, ...rest } = init ?? {};

The SSRF DNS validation still runs upstream in fetchWithSsrFGuard — the dispatcher was only needed for DNS pinning which already completed before the custom fetch is called.

Environment

  • OpenClaw: 2026.4.5
  • Node.js: v25.6.1
  • Platform: macOS arm64

Additional Note

The catch {} in resolveSlackMedia should ideally log the error rather than silently swallowing it, to make debugging easier.

extent analysis

TL;DR

Remove the dispatcher option from the fetch call in createSlackMediaFetch to fix the TypeError in Node.js v25.

Guidance

  • Identify the createSlackMediaFetch function in extensions/slack/src/actions.ts and update the fetch call to exclude the dispatcher option.
  • Verify that the fetchWithSsrFGuard still performs SSRF DNS validation upstream, ensuring security is maintained.
  • Consider logging the error in the catch block of resolveSlackMedia to improve debugging capabilities.
  • Test the updated code with the provided reproduction steps to confirm the fix.

Example

const { headers: initHeaders, redirect: _redirect, dispatcher: _dispatcher, ...rest } = init ?? {};
globalThis.fetch(url, { ...rest });

Notes

This fix assumes that the dispatcher option is not necessary for the globalThis.fetch call in Node.js v25, as the DNS pinning has already been completed.

Recommendation

Apply the workaround by removing the dispatcher option from the fetch call, as it is a targeted fix for the identified issue in Node.js v25.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING