Skip to content

shouldUseGlobalFetchAndWebSocket() returns false on Cloudflare Workers with nodejs_compat #11455

@Muhammad-Bin-Ali

Description

@Muhammad-Bin-Ali

Which package is this bug report for?

util

Issue description

shouldUseGlobalFetchAndWebSocket() fails to detect Cloudflare Workers when nodejs_compat (or nodejs_compat_v2) is enabled, causing client.login() to hang indefinitely with no error.

Root cause: Workers with nodejs_compat polyfills globalThis.process including process.versions.node (currently "22.19.0"). The function sees process exists, checks process.versions for deno/bun, finds neither, and returns false. This causes @discordjs/ws to use the ws npm package (which requires TCP sockets) instead of the native Web WebSocket API. Cloudflare Workers doesn't support raw TCP sockets, so the connection attempt silently hangs forever.

Steps to reproduce:

  1. Create a Cloudflare Worker with nodejs_compat or nodejs_compat_v2 compatibility flag
  2. Install discord.js (or @discordjs/ws + @discordjs/rest directly)
  3. Call client.login(token) or manager.connect()
  4. Connection hangs indefinitely — no error, no timeout, no crash

Irony: getUserAgentAppendix() in the same file (packages/util/src/functions/userAgentAppendix.ts) already detects Workers by checking globalThis.WebSocketPair, but shouldUseGlobalFetchAndWebSocket() has no Workers awareness.

Without nodejs_compat, Workers has no globalThis.process, so the function takes the first branch, sees fetch and WebSocket exist, and returns true — but this path is unusable anyway because @discordjs/ws imports node:buffer, node:events, and node:timers/promises at the top level, which require nodejs_compat.

Proposed fix: Check for globalThis.WebSocketPair (a Workers-only global — no other runtime exposes it) before the Deno/Bun branch. I've opened up a PR with what I believe is the fix

Code sample

// wrangler.toml for a Cloudflare Worker
// compatibility_flags = ["nodejs_compat_v2"]
import { Client, GatewayIntentBits } from 'discord.js';
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const client = new Client({ intents: [GatewayIntentBits.Guilds] });
    // This hangs forever — no error, no timeout
    // shouldUseGlobalFetchAndWebSocket() returns false because
    // nodejs_compat polyfills process.versions.node = "22.19.0"
    // so it falls through to using the ws npm package (TCP sockets),
    // which Workers doesn't support
    await client.login(env.DISCORD_TOKEN);
    return new Response('Bot started');
  },
};

Versions

  • @discordjs/util 1.1.1
  • @discordjs/ws 2.0.2
  • Cloudflare Workers (workerd) with nodejs_compat_v2
  • wrangler 4.x
  • TypeScript 5.9.3

Issue priority

Medium (should be fixed soon)

Which partials do you have configured?

Not applicable

Which gateway intents are you subscribing to?

Guilds

I have tested this issue on a development release

2a06721

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions