{"record":{"id":"6b245b15b8adc1f8","repo":"dotnet/runtime","slug":"please-install-node-fetch-and-node-abort-contro","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/mono/browser/runtime/http.ts","lineNumber":20,"sourceCode":"// The .NET Foundation licenses this file to you under the MIT license.\n\nimport BuildConfiguration from \"consts:configuration\";\n\nimport { wrap_as_cancelable_promise } from \"./cancelable-promise\";\nimport { ENVIRONMENT_IS_NODE, loaderHelpers, mono_assert } from \"./globals\";\nimport { assert_js_interop } from \"./invoke-js\";\nimport { MemoryViewType, Span } from \"./marshal\";\nimport type { VoidPtr } from \"./types/emscripten\";\nimport { ControllablePromise } from \"./types/internal\";\nimport { mono_log_debug } from \"./logging\";\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    assert_js_interop();\n    mono_assert(controller, \"expected controller\");\n}\n\nexport function http_wasm_supports_streaming_request (): 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.\n    // So, if that header is set, then we know the browser doesn't support streams in request objects, and we can exit early.\n    // 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.\n    // See https://developer.chrome.com/articles/fetch-streaming-requests/\n    if (typeof Request !== \"undefined\" && \"body\" in Request.prototype && typeof ReadableStream === \"function\" && typeof TransformStream === \"function\") {\n        let duplexAccessed = false;","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/dotnet/runtime/blob/290d5ab72cc1102813fbc0406fb186ceadabc340/src/mono/browser/runtime/http.ts#L2-L38","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Install the polyfills: `npm install node-fetch node-abort-controller`.","Upgrade to NodeJS 18+ which provides global fetch and AbortController natively, making the polyfills unnecessary.","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."],"exampleFix":"// before: booting runtime in Node 16 without polyfills\n// const dotnet = await createDotnetRuntime(opts); // throws on first HTTP call\n\n// after (Node <18)\nconst fetch = (await import('node-fetch')).default;\nconst { AbortController } = await import('node-abort-controller');\nglobalThis.fetch = fetch;\nglobalThis.AbortController = AbortController;\nconst dotnet = await createDotnetRuntime(opts);","handlingStrategy":"validation","validationCode":"// Detect missing Node fetch/AbortController before booting the runtime\nfunction nodeHttpReady() {\n  return typeof globalThis.fetch === 'function' && typeof globalThis.AbortController === 'function';\n}\nif (ENVIRONMENT_IS_NODE && !nodeHttpReady()) {\n  // install polyfills before createDotnetRuntime\n}","typeGuard":"function hasHttpPrimitives(): boolean {\n  return typeof globalThis.fetch === 'function' && typeof globalThis.AbortController === 'function';\n}","tryCatchPattern":null,"preventionTips":["Target Node 18+ in CI and locally to get fetch/AbortController natively.","List node-fetch and node-abort-controller as dependencies when supporting older Node.","Install polyfills at the very top of your entry script, before any runtime import."],"tags":["network","node","fetch","http","environment"],"analyzedSha":"290d5ab72cc1102813fbc0406fb186ceadabc340","analyzedAt":"2026-08-06T19:57:01.276Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}