{"record":{"id":"19c2d72a7d3d6c50","repo":"dotnet/runtime","slug":"please-install-node-fetch-and-node-abort-contro-19c2d7","errorCode":null,"errorMessage":"Please install `node-fetch` and `node-abort-controller` npm packages to enable HTTP client support.","messagePattern":"Please install `node-fetch` and `node-abort-controller` npm packages to enable HTTP client support\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/http.ts","lineNumber":18,"sourceCode":"// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\nimport BuildConfiguration from \"consts:configuration\";\n\nimport type { VoidPtr, ControllablePromise } from \"./types\";\n\nimport { wrapAsCancelablePromise } from \"./cancelable-promise\";\nimport { ENVIRONMENT_IS_NODE } from \"./per-module\";\nimport { assertJsInterop } from \"./utils\";\nimport { MemoryViewType, Span } from \"./marshaled-types\";\nimport { dotnetLogger, dotnetAssert } from \"./cross-module\";\n\n\nfunction verifyEnvironment() {\n    if (typeof globalThis.fetch !== \"function\" || typeof globalThis.AbortController !== \"function\") {\n        const message = ENVIRONMENT_IS_NODE\n            ? \"Please install `node-fetch` and `node-abort-controller` npm packages to enable HTTP client support.\"\n            : \"This browser doesn't support fetch API. Please use a modern browser. See also https://learn.microsoft.com/aspnet/core/blazor/supported-platforms\";\n        throw new Error(message);\n    }\n}\n\nfunction commonAsserts(controller: HttpController) {\n    if (BuildConfiguration !== \"Debug\") {\n        return;\n    }\n    assertJsInterop();\n    dotnetAssert.check(controller, \"expected controller\");\n}\n\nexport function httpSupportsStreamingRequest(): boolean {\n    // Detecting streaming request support works like this:\n    // If the browser doesn't support a particular body type, it calls toString() on the object and uses the result as the body.\n    // So, if the browser doesn't support request streams, the request body becomes the string \"[object ReadableStream]\".\n    // When a string is used as a body, it conveniently sets the Content-Type header to text/plain;charset=UTF-8.","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/dotnet/runtime/blob/290d5ab72cc1102813fbc0406fb186ceadabc340/src/native/libs/System.Runtime.InteropServices.JavaScript.Native/interop/http.ts#L1-L36","documentation":"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.","triggerScenarios":"A .NET WASM app using HttpClient under Node.js (server-side, unit tests, SSR, build scripts) where globalThis.fetch or globalThis.AbortController is undefined.","commonSituations":"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.","solutions":["Upgrade to Node 18+, which has global fetch and AbortController built in.","Or: npm install node-fetch node-abort-controller and assign them to globalThis before invoking any HTTP API."],"exampleFix":"// before (Node 16, no global fetch)\nvar ctrl = dotnet.INTERNAL.httpCreateController(); // throws\n\n// after\nconst fetch = require(\"node-fetch\"); globalThis.fetch = globalThis.fetch || fetch;\nconst { AbortController } = require(\"node-abort-controller\"); globalThis.AbortController = globalThis.AbortController || AbortController;\nvar ctrl = dotnet.INTERNAL.httpCreateController();","handlingStrategy":"validation","validationCode":"const isNode = typeof process !== \"undefined\" && process.versions?.node;\nif (isNode && (typeof globalThis.fetch !== \"function\" || typeof globalThis.AbortController !== \"function\")) {\n  console.error(\"Install node-fetch & node-abort-controller (or use Node >= 18) before using HttpClient.\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Target Node 18+ for .NET WASM HTTP work.","Register fetch/AbortController polyfills at the very top of the Node entry script, before runtime init."],"tags":["http","node-js","polyfill","fetch"],"analyzedSha":"290d5ab72cc1102813fbc0406fb186ceadabc340","analyzedAt":"2026-08-06T19:57:01.276Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}