Skip to content

fix(bluebubbles): route fetchImpl through bundled undici fetch on Node 24+#66108

Closed
coletebou wants to merge 1 commit intoopenclaw:mainfrom
coletebou:fix/bb-runtime-dispatcher-v3
Closed

fix(bluebubbles): route fetchImpl through bundled undici fetch on Node 24+#66108
coletebou wants to merge 1 commit intoopenclaw:mainfrom
coletebou:fix/bb-runtime-dispatcher-v3

Conversation

@coletebou
Copy link
Copy Markdown
Contributor

Summary

BlueBubbles attachment downloads fail on Node 24+ because the SSRF guard's pinned-DNS dispatcher is an undici 8.x Agent, but Node 24's built-in globalThis.fetch uses undici 7.x internally. Passing the 8.x dispatcher causes "invalid onRequestStart method" errors.

Routes through fetchWithRuntimeDispatcher (bundled undici's own fetch) when a dispatcher is present in init, matching the approach used by the Slack extension (#62239).

Supersedes #66103 and #63984.

Changes

  • extensions/bluebubbles/src/attachments.ts:
    • Use fetchWithRuntimeDispatcher when init contains a dispatcher (preserves SSRF DNS pinning)
    • Add isMockedFetch guard so test-installed globalThis.fetch mocks are honoured (parity with Slack)
    • Forward opts.timeoutMs (default 10s) via AbortSignal.timeout — no timeout regression
    • Remove unused blueBubblesFetchWithTimeout import

Test plan

  • Send an image to a BB-connected agent on Node 24+ — confirm attachment downloads
  • Verify no SSRF guard regressions (DNS pinning preserved)
  • Confirm stalled downloads still timeout after 10s (or configured timeoutMs)
  • Verify existing tests pass (isMockedFetch guard preserves mock behaviour)

…hment downloads on Node 24+

The SSRF guard pinned-DNS dispatcher is an undici 8.x Agent, but Node 24
built-in globalThis.fetch uses undici 7.x internally. Passing the 8.x
dispatcher to native fetch causes 'invalid onRequestStart method' errors,
breaking all BlueBubbles attachment downloads.

Route through fetchWithRuntimeDispatcher (bundled undici own fetch) when
a dispatcher is present, matching the Slack extension (openclaw#62239). Preserves
SSRF DNS pinning, forwards opts.timeoutMs via AbortSignal.timeout (default
10s), and honours test-installed globalThis.fetch mocks via isMockedFetch
guard.
@openclaw-barnacle openclaw-barnacle Bot added channel: bluebubbles Channel integration: bluebubbles size: XS labels Apr 13, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 13, 2026

Greptile Summary

Fixes attachment download failures on Node 24+ by routing the fetchImpl callback through fetchWithRuntimeDispatcher (bundled undici) when the SSRF guard has attached a DNS-pinning dispatcher to init, matching the pattern already used in the Slack and Matrix extensions. The timeout previously handled by blueBubblesFetchWithTimeout is preserved via AbortSignal.timeout.

Confidence Score: 5/5

Safe to merge — the fix is a targeted, well-precedented compatibility patch with no P0/P1 issues.

The dispatcher-routing logic exactly mirrors the Slack and Matrix extensions, fetchWithSsrFGuard does not inject a timeout signal (so AbortSignal.timeout fires as expected), and the isMockedFetch guard preserves test-mock behaviour. The only finding is a P2 style note about a locally duplicated helper.

No files require special attention.

Fix All in Codex Fix All in Claude Code

Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/bluebubbles/src/attachments.ts
Line: 97-102

Comment:
**Duplicated `isMockedFetch` helper — third copy in the codebase**

An identical implementation already lives in `extensions/slack/src/monitor/media.ts:66-70` and `extensions/matrix/src/matrix/sdk/transport.ts:94-98`. The function is not currently exported from `openclaw/plugin-sdk/infra-runtime` (the SDK re-exports `fetchWithRuntimeDispatcher` from `fetch-guard`, but `isMockedFetch` is only exported from `runtime-fetch.ts` without a re-export). If this pattern is needed by a fourth extension, consider promoting `isMockedFetch` to the SDK instead of another copy. No action required to merge — the local copy is safe and consistent with the Slack/Matrix approach.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(bluebubbles): route fetchImpl throug..." | Re-trigger Greptile

Comment on lines +97 to +102
function isMockedFetch(fetchImpl: typeof fetch | undefined): boolean {
if (typeof fetchImpl !== "function") {
return false;
}
return typeof (fetchImpl as typeof fetch & { mock?: unknown }).mock === "object";
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Duplicated isMockedFetch helper — third copy in the codebase

An identical implementation already lives in extensions/slack/src/monitor/media.ts:66-70 and extensions/matrix/src/matrix/sdk/transport.ts:94-98. The function is not currently exported from openclaw/plugin-sdk/infra-runtime (the SDK re-exports fetchWithRuntimeDispatcher from fetch-guard, but isMockedFetch is only exported from runtime-fetch.ts without a re-export). If this pattern is needed by a fourth extension, consider promoting isMockedFetch to the SDK instead of another copy. No action required to merge — the local copy is safe and consistent with the Slack/Matrix approach.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/bluebubbles/src/attachments.ts
Line: 97-102

Comment:
**Duplicated `isMockedFetch` helper — third copy in the codebase**

An identical implementation already lives in `extensions/slack/src/monitor/media.ts:66-70` and `extensions/matrix/src/matrix/sdk/transport.ts:94-98`. The function is not currently exported from `openclaw/plugin-sdk/infra-runtime` (the SDK re-exports `fetchWithRuntimeDispatcher` from `fetch-guard`, but `isMockedFetch` is only exported from `runtime-fetch.ts` without a re-export). If this pattern is needed by a fourth extension, consider promoting `isMockedFetch` to the SDK instead of another copy. No action required to merge — the local copy is safe and consistent with the Slack/Matrix approach.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Codex Fix in Claude Code

@omarshahine
Copy link
Copy Markdown
Contributor

Superseded by #67510 (merged 2026-04-16). Your diagnosis of the bundled-undici dispatcher breakage on modern Node was on target; #67510's fix #1 takes a different approach (strip the dispatcher from the non-SSRF fallback path in blueBubblesFetchWithTimeout) but addresses the same root cause. Thanks — closing in favor of the merged fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: bluebubbles Channel integration: bluebubbles size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants