Summary
Inbound BlueBubbles image attachments silently fail to download. No files appear in ~/.openclaw/media/inbound/. The agent receives the message text/placeholder but cannot see the image.
Environment
- OpenClaw 2026.4.9
- Node.js 22.22.2
- macOS 26.4.0
- BlueBubbles server on
http://127.0.0.1:1234
- Config:
channels.bluebubbles.network.dangerouslyAllowPrivateNetwork: true
Root cause
fetchWithSsrFGuard creates a pinned DNS dispatcher using OpenClaw's bundled undici and passes it via init.dispatcher to the custom fetchImpl in downloadBlueBubblesAttachment. That fetchImpl delegates to blueBubblesFetchWithTimeout (the non-ssrfPolicy branch at the bottom of the function), which calls globalThis.fetch() — backed by Node.js 22's built-in undici.
Node's built-in undici rejects the foreign dispatcher:
TypeError: fetch failed
cause: invalid onRequestStart method (UND_ERR_INVALID_ARG)
The error is swallowed by the try/catch in processMessage (channel.runtime) and only logged at verbose level, making it appear as though attachments simply aren't present.
Affected code
In the source for blueBubblesFetchWithTimeout (likely extensions/bluebubbles/src/types.ts), the else branch that runs when ssrfPolicy is undefined:
// ssrfPolicy is undefined when called from the fetchImpl wrapper
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
return await fetch(url, {
...init, // ← init.dispatcher is from bundled undici
signal: controller.signal,
});
} finally {
clearTimeout(timer);
}
Fix
Strip dispatcher from init before passing to the global fetch(). The SSRF validation has already passed in the outer fetchWithSsrFGuard call, so the dispatcher is not needed here:
const { dispatcher: _d, ...safeInit } = init ?? {};
return await fetch(url, {
...safeInit,
signal: controller.signal,
});
Broader note
The supportsDispatcherInit heuristic in fetchWithSsrFGuard assumes that any non-global custom fetchImpl supports the bundled undici dispatcher. But downloadBlueBubblesAttachment's fetchImpl delegates to globalThis.fetch, which uses a different undici version. Any other custom fetchImpl that does the same will hit this incompatibility.
An alternative fix at the fetchWithSsrFGuard level would be to use fetchWithRuntimeDispatcher (which uses the bundled undici fetch directly) whenever there is a dispatcher, regardless of whether a custom fetchImpl is provided.
Reproduction
- Configure BlueBubbles with a local server URL (e.g.,
http://127.0.0.1:1234)
- Set
channels.bluebubbles.network.dangerouslyAllowPrivateNetwork: true
- Send an image via iMessage to a chat monitored by OpenClaw
- Observe that
~/.openclaw/media/inbound/ remains empty
- Enable verbose logging to see the
TypeError in the attachment download catch block
Summary
Inbound BlueBubbles image attachments silently fail to download. No files appear in
~/.openclaw/media/inbound/. The agent receives the message text/placeholder but cannot see the image.Environment
http://127.0.0.1:1234channels.bluebubbles.network.dangerouslyAllowPrivateNetwork: trueRoot cause
fetchWithSsrFGuardcreates a pinned DNS dispatcher using OpenClaw's bundled undici and passes it viainit.dispatcherto the customfetchImplindownloadBlueBubblesAttachment. ThatfetchImpldelegates toblueBubblesFetchWithTimeout(the non-ssrfPolicybranch at the bottom of the function), which callsglobalThis.fetch()— backed by Node.js 22's built-in undici.Node's built-in undici rejects the foreign dispatcher:
The error is swallowed by the
try/catchinprocessMessage(channel.runtime) and only logged at verbose level, making it appear as though attachments simply aren't present.Affected code
In the source for
blueBubblesFetchWithTimeout(likelyextensions/bluebubbles/src/types.ts), theelsebranch that runs whenssrfPolicyisundefined:Fix
Strip
dispatcherfrominitbefore passing to the globalfetch(). The SSRF validation has already passed in the outerfetchWithSsrFGuardcall, so the dispatcher is not needed here:Broader note
The
supportsDispatcherInitheuristic infetchWithSsrFGuardassumes that any non-global customfetchImplsupports the bundled undici dispatcher. ButdownloadBlueBubblesAttachment'sfetchImpldelegates toglobalThis.fetch, which uses a different undici version. Any other customfetchImplthat does the same will hit this incompatibility.An alternative fix at the
fetchWithSsrFGuardlevel would be to usefetchWithRuntimeDispatcher(which uses the bundled undici fetch directly) whenever there is a dispatcher, regardless of whether a customfetchImplis provided.Reproduction
http://127.0.0.1:1234)channels.bluebubbles.network.dangerouslyAllowPrivateNetwork: true~/.openclaw/media/inbound/remains emptyTypeErrorin the attachment download catch block