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() checks that globalThis.fetch and globalThis.AbortController exist before any HttpClient operation. On Node.js (ENVIRONMENT_IS_NODE) these are absent in older Node versions, so the message instructs installing node-fetch and node-abort-controller as polyfills. httpCreateController() and httpFetch() both call verifyEnvironment().
Solutions
- Install the polyfills: `npm install node-fetch node-abort-controller` and import them so they attach to globalThis before the runtime initializes (import 'node-abort-controller/polyfill'; import { fetch } from 'node-fetch'; globalThis.fetch = fetch;).
- Upgrade to Node.js >= 18 where fetch and AbortController are available globally and the polyfills are unnecessary.
- If you cannot install packages, gate HttpClient usage to environments that have fetch, and avoid HTTP calls from Node-hosted WASM.
Example fix
// before
// Node 16, .NET WASM calls HttpClient -> throws
// after (Node < 18)
import 'node-abort-controller/polyfill';
import { fetch, Headers, Request, Response } from 'node-fetch';
globalThis.fetch = fetch as any;
globalThis.Headers = Headers as any;
globalThis.Request = Request as any;
globalThis.Response = Response as any;
// then boot the runtime Defensive patterns
Strategy: validation
Validate before calling
// Detect missing fetch/AbortController under Node before booting the runtime.
function ensureFetchPolyfill() {
if (typeof globalThis.fetch !== "function" || typeof globalThis.AbortController !== "function") {
// Node < 18: require polyfills
require("node-abort-controller/polyfill");
const { fetch, Headers, Request, Response } = require("node-fetch");
Object.assign(globalThis, { fetch, Headers, Request, Response });
}
}
ensureFetchPolyfill(); Type guard
function hasFetch(): boolean {
return typeof globalThis.fetch === "function" && typeof globalThis.AbortController === "function";
} Try / catch
null
Prevention
- Install node-fetch and node-abort-controller in any Node-hosted WASM project on Node < 18.
- Prefer Node >= 18 which has native fetch and AbortController.
- Import polyfills before initializing the dotnet runtime.
- Gate HttpClient usage behind a hasFetch() feature check.
When it happens
Trigger: Using System.Net.Http.HttpClient (or HttpClientHandler streaming) from .NET WASM running under Node.js where global fetch/AbortController are missing, without having loaded the polyfills onto globalThis first.
Common situations: Server-side prerendering or SSR of a Blazor/JSInterop app under Node < 18; unit tests that run the WASM runtime in Node without installing fetch polyfills; an export-to-PDF or background service that uses HttpClient from .NET-in-Node.
Related errors
- No fetch implementation available
- Please install `node-fetch` and `node-abort-controller` npm…
- No fetch implementation available
- NotImplementedException
- This browser doesn't support fetch API. Please use a modern…
AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10).
Data as JSON: /api/errors/19c2d72a7d3d6c50.
Report an issue: GitHub.
Appendix: source
Thrown at src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/http.ts:20
// 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.
// 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.View on GitHub (pinned to 60108ba66e)