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

verifyEnvironment (called by httpCreateController and httpFetch) checks for global fetch and AbortController. On Node.js older than 18 these are not built in, so the .NET WASM HttpClient needs the node-fetch and node-abort-controller polyfills; without them any HTTP client use throws immediately.

Source

Thrown at src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/http.ts:18

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import BuildConfiguration from "consts:configuration";

import type { VoidPtr, ControllablePromise } from "./types";

import { wrapAsCancelablePromise } from "./cancelable-promise";
import { ENVIRONMENT_IS_NODE } from "./per-module";
import { assertJsInterop } from "./utils";
import { MemoryViewType, Span } from "./marshaled-types";
import { dotnetLogger, dotnetAssert } from "./cross-module";


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) {
    if (BuildConfiguration !== "Debug") {
        return;
    }
    assertJsInterop();
    dotnetAssert.check(controller, "expected controller");
}

export function httpSupportsStreamingRequest(): 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.

View on GitHub (pinned to 290d5ab72c)

Solutions

  1. Upgrade to Node 18+, which has global fetch and AbortController built in.
  2. Or: npm install node-fetch node-abort-controller and assign them to globalThis before invoking any HTTP API.

Example fix

// before (Node 16, no global fetch)
var ctrl = dotnet.INTERNAL.httpCreateController(); // throws

// after
const fetch = require("node-fetch"); globalThis.fetch = globalThis.fetch || fetch;
const { AbortController } = require("node-abort-controller"); globalThis.AbortController = globalThis.AbortController || AbortController;
var ctrl = dotnet.INTERNAL.httpCreateController();
Defensive patterns

Strategy: validation

Validate before calling

const isNode = typeof process !== "undefined" && process.versions?.node;
if (isNode && (typeof globalThis.fetch !== "function" || typeof globalThis.AbortController !== "function")) {
  console.error("Install node-fetch & node-abort-controller (or use Node >= 18) before using HttpClient.");
}

Prevention

When it happens

Trigger: A .NET WASM app using HttpClient under Node.js (server-side, unit tests, SSR, build scripts) where globalThis.fetch or globalThis.AbortController is undefined.

Common situations: Node 14/16 hosting; Node-based test runners; forgetting to register the polyfills before the runtime initializes HTTP; SSR/prerender pipelines running on older Node.

Related errors


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