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
- Have a Slack file attachment in any channel
- Call
message(action="download-file", channel="slack", fileId="F...")
- 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.
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 hasfiles:readscope.Root Cause
createSlackMediaFetchinextensions/slack/src/actions.tscallsglobalThis.fetch()and passes through thedispatcheroption from the SSRF guard'sfetchWithSsrFGuard. In Node.js v25,globalThis.fetchdoes not support thedispatcheroption (onlyundici.fetchdoes), causing aTypeError: fetch failed.The error is silently swallowed by the
catch {}inresolveSlackMedia, making the download returnnullwith no logging.Reproduction
message(action="download-file", channel="slack", fileId="F...")files.infoAPI and directcurlwith the bot token both work fineFix
Strip
dispatcherfrom the options before passing toglobalThis.fetchincreateSlackMediaFetch: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
Additional Note
The
catch {}inresolveSlackMediashould ideally log the error rather than silently swallowing it, to make debugging easier.