dotnet/runtime · error · Error

Please install `node-fetch` and `node-abort-controller` npm

Error message

Please install `node-fetch` and `node-abort-controller` npm packages to enable HTTP client support.

What it means

Thrown by verifyEnvironment() in http.ts when running under NodeJS and globalThis.fetch or globalThis.AbortController is missing. The .NET WASM HTTP client requires both primitives; on older Node versions they are not built-in and must be polyfilled via node-fetch and node-abort-controller.

Source

Thrown at src/mono/browser/runtime/http.ts:20

// The .NET Foundation licenses this file to you under the MIT license.

import BuildConfiguration from "consts:configuration";

import { wrap_as_cancelable_promise } from "./cancelable-promise";
import { ENVIRONMENT_IS_NODE, loaderHelpers, mono_assert } from "./globals";
import { assert_js_interop } from "./invoke-js";
import { MemoryViewType, Span } from "./marshal";
import type { VoidPtr } from "./types/emscripten";
import { ControllablePromise } from "./types/internal";
import { mono_log_debug } from "./logging";


function verifyEnvironment () {
    if (typeof globalThis.fetch !== "function" || typeof globalThis.AbortController !== "function") {
        const message = ENVIRONMENT_IS_NODE
            ? "Please install `node-fetch` and `node-abort-controller` npm packages to enable HTTP client support."
            : "This browser doesn't support fetch API. Please use a modern browser. See also https://learn.microsoft.com/aspnet/core/blazor/supported-platforms";
        throw new Error(message);
    }
}

function commonAsserts (controller: HttpController) {
    assert_js_interop();
    mono_assert(controller, "expected controller");
}

export function http_wasm_supports_streaming_request (): boolean {
    // Detecting streaming request support works like this:
    // If the browser doesn't support a particular body type, it calls toString() on the object and uses the result as the body.
    // So, if the browser doesn't support request streams, the request body becomes the string "[object ReadableStream]".
    // When a string is used as a body, it conveniently sets the Content-Type header to text/plain;charset=UTF-8.
    // So, if that header is set, then we know the browser doesn't support streams in request objects, and we can exit early.
    // Safari does support streams in request objects, but doesn't allow them to be used with fetch, so the duplex option is tested, which Safari doesn't currently support.
    // See https://developer.chrome.com/articles/fetch-streaming-requests/
    if (typeof Request !== "undefined" && "body" in Request.prototype && typeof ReadableStream === "function" && typeof TransformStream === "function") {
        let duplexAccessed = false;

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Install the polyfills: `npm install node-fetch node-abort-controller`.
  2. Upgrade to NodeJS 18+ which provides global fetch and AbortController natively, making the polyfills unnecessary.
  3. If you cannot upgrade Node, import and assign them: `globalThis.fetch = require('node-fetch'); globalThis.AbortController = require('node-abort-controller').AbortController;` before booting the runtime.

Example fix

// before: booting runtime in Node 16 without polyfills
// const dotnet = await createDotnetRuntime(opts); // throws on first HTTP call

// after (Node <18)
const fetch = (await import('node-fetch')).default;
const { AbortController } = await import('node-abort-controller');
globalThis.fetch = fetch;
globalThis.AbortController = AbortController;
const dotnet = await createDotnetRuntime(opts);
Defensive patterns

Strategy: validation

Validate before calling

// Detect missing Node fetch/AbortController before booting the runtime
function nodeHttpReady() {
  return typeof globalThis.fetch === 'function' && typeof globalThis.AbortController === 'function';
}
if (ENVIRONMENT_IS_NODE && !nodeHttpReady()) {
  // install polyfills before createDotnetRuntime
}

Type guard

function hasHttpPrimitives(): boolean {
  return typeof globalThis.fetch === 'function' && typeof globalThis.AbortController === 'function';
}

Prevention

When it happens

Trigger: Produced the first time the runtime creates an HTTP controller (http_wasm_create_controller) or calls http_wasm_fetch while ENVIRONMENT_IS_NODE is true and either fetch or AbortController is undefined.

Common situations: Running .NET WASM under NodeJS older than 18 (which lacks global fetch/AbortController); running tests or SSR in Node without installing the polyfills; upgrading the runtime and forgetting the Node polyfill deps.

Related errors


AI-assisted analysis of dotnet/runtime@290d5ab72c (2026-08-06). Data as JSON: /api/errors/6b245b15b8adc1f8. Report an issue: GitHub.